pax_global_header00006660000000000000000000000064146461567570014537gustar00rootroot0000000000000052 comment=3159dba933718aac6f3636af8f6af7301236e200 neko-2-4-0/000077500000000000000000000000001464615675700124745ustar00rootroot00000000000000neko-2-4-0/.devcontainer/000077500000000000000000000000001464615675700152335ustar00rootroot00000000000000neko-2-4-0/.devcontainer/build.sh000077500000000000000000000006121464615675700166700ustar00rootroot00000000000000#!/bin/bash set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" IMAGE="haxe/neko_devcontainer" TAG="$IMAGE:$(date +%Y%m%d%H%M%S)" set -x docker login docker buildx use neko || docker buildx create --use --name neko docker buildx build --pull --platform linux/amd64,linux/arm64 --tag "$TAG" --push "$DIR" sed -i -e "s#$IMAGE:[0-9]*#$TAG#g" "$DIR/docker-compose.yml" neko-2-4-0/.devcontainer/devcontainer.json000066400000000000000000000015031464615675700206060ustar00rootroot00000000000000// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.202.5/containers/docker-from-docker-compose { "name": "neko", "dockerComposeFile": "docker-compose.yml", "service": "workspace", "workspaceFolder": "/workspace", // Use this environment variable if you need to bind mount your local source code into a new container. "remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }, // Set *default* container specific settings.json values on container create. "settings": {}, "extensions": [ "ms-azuretools.vscode-docker", "twxs.cmake", "ms-vscode.cpptools", "ms-vscode.cpptools-themes", "jeff-hykin.better-cpp-syntax", "earthly.earthfile-syntax-highlighting", ], "remoteUser": "vscode" }neko-2-4-0/.devcontainer/direnv.toml000066400000000000000000000000451464615675700174160ustar00rootroot00000000000000[whitelist] prefix = [ "/workspace" ]neko-2-4-0/.devcontainer/docker-compose.yml000066400000000000000000000016671464615675700207020ustar00rootroot00000000000000version: '3' services: workspace: # To update, use `earthly --push +devcontainer-rebuild` image: haxe/neko_devcontainer:20221124162640 init: true volumes: - ..:/workspace:cached # Forwards the local Docker socket to the container. - /var/run/docker.sock:/var/run/docker-host.sock environment: - EARTHLY_BUILDKIT_HOST=tcp://earthly:8372 # - EARTHLY_SECRET_FILES=.envrc=/workspace/.envrc - EARTHLY_USE_INLINE_CACHE=true - EARTHLY_SAVE_INLINE_CACHE=true entrypoint: /usr/local/share/docker-init.sh command: sleep infinity user: vscode earthly: image: earthly/buildkitd:v0.6.30 privileged: true environment: - BUILDKIT_TCP_TRANSPORT_ENABLED=true expose: - 8372 volumes: # https://docs.earthly.dev/docs/guides/using-the-earthly-docker-images/buildkit-standalone#earthly_tmp_dir - earthly-tmp:/tmp/earthly:rw volumes: earthly-tmp: neko-2-4-0/.devcontainer/library-scripts/000077500000000000000000000000001464615675700203645ustar00rootroot00000000000000neko-2-4-0/.devcontainer/library-scripts/common-debian.sh000066400000000000000000000443361464615675700234420ustar00rootroot00000000000000#!/usr/bin/env bash #------------------------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. #------------------------------------------------------------------------------------------------------------- # # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/common.md # Maintainer: The VS Code and Codespaces Teams # # Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag] [install Oh My Zsh! flag] [Add non-free packages] set -e INSTALL_ZSH=${1:-"true"} USERNAME=${2:-"automatic"} USER_UID=${3:-"automatic"} USER_GID=${4:-"automatic"} UPGRADE_PACKAGES=${5:-"true"} INSTALL_OH_MYS=${6:-"true"} ADD_NON_FREE_PACKAGES=${7:-"false"} SCRIPT_DIR="$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)" MARKER_FILE="/usr/local/etc/vscode-dev-containers/common" if [ "$(id -u)" -ne 0 ]; then echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' exit 1 fi # Ensure that login shells get the correct path if the user updated the PATH using ENV. rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh # If in automatic mode, determine if a user already exists, if not use vscode if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then USERNAME="" POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") for CURRENT_USER in ${POSSIBLE_USERS[@]}; do if id -u ${CURRENT_USER} > /dev/null 2>&1; then USERNAME=${CURRENT_USER} break fi done if [ "${USERNAME}" = "" ]; then USERNAME=vscode fi elif [ "${USERNAME}" = "none" ]; then USERNAME=root USER_UID=0 USER_GID=0 fi # Load markers to see which steps have already run if [ -f "${MARKER_FILE}" ]; then echo "Marker file found:" cat "${MARKER_FILE}" source "${MARKER_FILE}" fi # Ensure apt is in non-interactive to avoid prompts export DEBIAN_FRONTEND=noninteractive # Function to call apt-get if needed apt_get_update_if_needed() { if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then echo "Running apt-get update..." apt-get update else echo "Skipping apt-get update." fi } # Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then package_list="apt-utils \ openssh-client \ gnupg2 \ dirmngr \ iproute2 \ procps \ lsof \ htop \ net-tools \ psmisc \ curl \ wget \ rsync \ ca-certificates \ unzip \ zip \ nano \ vim-tiny \ less \ jq \ lsb-release \ apt-transport-https \ dialog \ libc6 \ libgcc1 \ libkrb5-3 \ libgssapi-krb5-2 \ libicu[0-9][0-9] \ liblttng-ust0 \ libstdc++6 \ zlib1g \ locales \ sudo \ ncdu \ man-db \ strace \ manpages \ manpages-dev \ init-system-helpers" # Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then # Bring in variables from /etc/os-release like VERSION_CODENAME . /etc/os-release sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list sed -i -E "s/deb-src http:\/\/(deb|httredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list sed -i -E "s/deb-src http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list # Handle bullseye location for security https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list echo "Running apt-get update..." apt-get update package_list="${package_list} manpages-posix manpages-posix-dev" else apt_get_update_if_needed fi # Install libssl1.1 if available if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then package_list="${package_list} libssl1.1" fi # Install appropriate version of libssl1.0.x if available libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '') if [ "$(echo "$LIlibssl_packageBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then # Debian 9 package_list="${package_list} libssl1.0.2" elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then # Ubuntu 18.04, 16.04, earlier package_list="${package_list} libssl1.0.0" fi fi echo "Packages to verify are installed: ${package_list}" apt-get -y install --no-install-recommends ${package_list} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 ) # Install git if not already installed (may be more recent than distro version) if ! type git > /dev/null 2>&1; then apt-get -y install --no-install-recommends git fi PACKAGES_ALREADY_INSTALLED="true" fi # Get to latest versions of all packages if [ "${UPGRADE_PACKAGES}" = "true" ]; then apt_get_update_if_needed apt-get -y upgrade --no-install-recommends apt-get autoremove -y fi # Ensure at least the en_US.UTF-8 UTF-8 locale is available. # Common need for both applications and things like the agnoster ZSH theme. if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen locale-gen LOCALE_ALREADY_SET="true" fi # Create or update a non-root user to match UID/GID. group_name="${USERNAME}" if id -u ${USERNAME} > /dev/null 2>&1; then # User exists, update if needed if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -g $USERNAME)" ]; then group_name="$(id -gn $USERNAME)" groupmod --gid $USER_GID ${group_name} usermod --gid $USER_GID $USERNAME fi if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then usermod --uid $USER_UID $USERNAME fi else # Create user if [ "${USER_GID}" = "automatic" ]; then groupadd $USERNAME else groupadd --gid $USER_GID $USERNAME fi if [ "${USER_UID}" = "automatic" ]; then useradd -s /bin/bash --gid $USERNAME -m $USERNAME else useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME fi fi # Add add sudo support for non-root user if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME chmod 0440 /etc/sudoers.d/$USERNAME EXISTING_NON_ROOT_USER="${USERNAME}" fi # ** Shell customization section ** if [ "${USERNAME}" = "root" ]; then user_rc_path="/root" else user_rc_path="/home/${USERNAME}" fi # Restore user .bashrc defaults from skeleton file if it doesn't exist or is empty if [ ! -f "${user_rc_path}/.bashrc" ] || [ ! -s "${user_rc_path}/.bashrc" ] ; then cp /etc/skel/.bashrc "${user_rc_path}/.bashrc" fi # Restore user .profile defaults from skeleton file if it doesn't exist or is empty if [ ! -f "${user_rc_path}/.profile" ] || [ ! -s "${user_rc_path}/.profile" ] ; then cp /etc/skel/.profile "${user_rc_path}/.profile" fi # .bashrc/.zshrc snippet rc_snippet="$(cat << 'EOF' if [ -z "${USER}" ]; then export USER=$(whoami); fi if [[ "${PATH}" != *"$HOME/.local/bin"* ]]; then export PATH="${PATH}:$HOME/.local/bin"; fi # Display optional first run image specific notice if configured and terminal is interactive if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "codespaces" ]] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then if [ -f "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" ]; then cat "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" elif [ -f "/workspaces/.codespaces/shared/first-run-notice.txt" ]; then cat "/workspaces/.codespaces/shared/first-run-notice.txt" fi mkdir -p "$HOME/.config/vscode-dev-containers" # Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it ((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &) fi # Set the default git editor if not already set if [ -z "$(git config --get core.editor)" ] && [ -z "${GIT_EDITOR}" ]; then if [ "${TERM_PROGRAM}" = "vscode" ]; then if [[ -n $(command -v code-insiders) && -z $(command -v code) ]]; then export GIT_EDITOR="code-insiders --wait" else export GIT_EDITOR="code --wait" fi fi fi EOF )" # code shim, it fallbacks to code-insiders if code is not available cat << 'EOF' > /usr/local/bin/code #!/bin/sh get_in_path_except_current() { which -a "$1" | grep -A1 "$0" | grep -v "$0" } code="$(get_in_path_except_current code)" if [ -n "$code" ]; then exec "$code" "$@" elif [ "$(command -v code-insiders)" ]; then exec code-insiders "$@" else echo "code or code-insiders is not installed" >&2 exit 127 fi EOF chmod +x /usr/local/bin/code # systemctl shim - tells people to use 'service' if systemd is not running cat << 'EOF' > /usr/local/bin/systemctl #!/bin/sh set -e if [ -d "/run/systemd/system" ]; then exec /bin/systemctl/systemctl "$@" else echo '\n"systemd" is not running in this container due to its overhead.\nUse the "service" command to start services intead. e.g.: \n\nservice --status-all' fi EOF chmod +x /usr/local/bin/systemctl # Codespaces bash and OMZ themes - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme codespaces_bash="$(cat \ <<'EOF' # Codespaces bash prompt theme __bash_prompt() { local userpart='`export XIT=$? \ && [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \ && [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`' local gitbranch='`\ if [ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ]; then \ export BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null); \ if [ "${BRANCH}" != "" ]; then \ echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \ && if git ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \ echo -n " \[\033[1;33m\]✗"; \ fi \ && echo -n "\[\033[0;36m\]) "; \ fi; \ fi`' local lightblue='\[\033[1;34m\]' local removecolor='\[\033[0m\]' PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ " unset -f __bash_prompt } __bash_prompt EOF )" codespaces_zsh="$(cat \ <<'EOF' # Codespaces zsh prompt theme __zsh_prompt() { local prompt_username if [ ! -z "${GITHUB_USER}" ]; then prompt_username="@${GITHUB_USER}" else prompt_username="%n" fi PROMPT="%{$fg[green]%}${prompt_username} %(?:%{$reset_color%}➜ :%{$fg_bold[red]%}➜ )" # User/exit code arrow PROMPT+='%{$fg_bold[blue]%}%(5~|%-1~/…/%3~|%4~)%{$reset_color%} ' # cwd PROMPT+='$([ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ] && git_prompt_info)' # Git status PROMPT+='%{$fg[white]%}$ %{$reset_color%}' unset -f __zsh_prompt } ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[cyan]%}(%{$fg_bold[red]%}" ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} " ZSH_THEME_GIT_PROMPT_DIRTY=" %{$fg_bold[yellow]%}✗%{$fg_bold[cyan]%})" ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg_bold[cyan]%})" __zsh_prompt EOF )" # Add RC snippet and custom bash prompt if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then echo "${rc_snippet}" >> /etc/bash.bashrc echo "${codespaces_bash}" >> "${user_rc_path}/.bashrc" echo 'export PROMPT_DIRTRIM=4' >> "${user_rc_path}/.bashrc" if [ "${USERNAME}" != "root" ]; then echo "${codespaces_bash}" >> "/root/.bashrc" echo 'export PROMPT_DIRTRIM=4' >> "/root/.bashrc" fi chown ${USERNAME}:${group_name} "${user_rc_path}/.bashrc" RC_SNIPPET_ALREADY_ADDED="true" fi # Optionally install and configure zsh and Oh My Zsh! if [ "${INSTALL_ZSH}" = "true" ]; then if ! type zsh > /dev/null 2>&1; then apt_get_update_if_needed apt-get install -y zsh fi if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then echo "${rc_snippet}" >> /etc/zsh/zshrc ZSH_ALREADY_INSTALLED="true" fi # Adapted, simplified inline Oh My Zsh! install steps that adds, defaults to a codespaces theme. # See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for official script. oh_my_install_dir="${user_rc_path}/.oh-my-zsh" if [ ! -d "${oh_my_install_dir}" ] && [ "${INSTALL_OH_MYS}" = "true" ]; then template_path="${oh_my_install_dir}/templates/zshrc.zsh-template" user_rc_file="${user_rc_path}/.zshrc" umask g-w,o-w mkdir -p ${oh_my_install_dir} git clone --depth=1 \ -c core.eol=lf \ -c core.autocrlf=false \ -c fsck.zeroPaddedFilemode=ignore \ -c fetch.fsck.zeroPaddedFilemode=ignore \ -c receive.fsck.zeroPaddedFilemode=ignore \ "https://github.com/ohmyzsh/ohmyzsh" "${oh_my_install_dir}" 2>&1 echo -e "$(cat "${template_path}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${user_rc_file} sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="codespaces"/g' ${user_rc_file} mkdir -p ${oh_my_install_dir}/custom/themes echo "${codespaces_zsh}" > "${oh_my_install_dir}/custom/themes/codespaces.zsh-theme" # Shrink git while still enabling updates cd "${oh_my_install_dir}" git repack -a -d -f --depth=1 --window=1 # Copy to non-root user if one is specified if [ "${USERNAME}" != "root" ]; then cp -rf "${user_rc_file}" "${oh_my_install_dir}" /root chown -R ${USERNAME}:${group_name} "${user_rc_path}" fi fi fi # Persist image metadata info, script if meta.env found in same directory meta_info_script="$(cat << 'EOF' #!/bin/sh . /usr/local/etc/vscode-dev-containers/meta.env # Minimal output if [ "$1" = "version" ] || [ "$1" = "image-version" ]; then echo "${VERSION}" exit 0 elif [ "$1" = "release" ]; then echo "${GIT_REPOSITORY_RELEASE}" exit 0 elif [ "$1" = "content" ] || [ "$1" = "content-url" ] || [ "$1" = "contents" ] || [ "$1" = "contents-url" ]; then echo "${CONTENTS_URL}" exit 0 fi #Full output echo echo "Development container image information" echo if [ ! -z "${VERSION}" ]; then echo "- Image version: ${VERSION}"; fi if [ ! -z "${DEFINITION_ID}" ]; then echo "- Definition ID: ${DEFINITION_ID}"; fi if [ ! -z "${VARIANT}" ]; then echo "- Variant: ${VARIANT}"; fi if [ ! -z "${GIT_REPOSITORY}" ]; then echo "- Source code repository: ${GIT_REPOSITORY}"; fi if [ ! -z "${GIT_REPOSITORY_RELEASE}" ]; then echo "- Source code release/branch: ${GIT_REPOSITORY_RELEASE}"; fi if [ ! -z "${BUILD_TIMESTAMP}" ]; then echo "- Timestamp: ${BUILD_TIMESTAMP}"; fi if [ ! -z "${CONTENTS_URL}" ]; then echo && echo "More info: ${CONTENTS_URL}"; fi echo EOF )" if [ -f "${SCRIPT_DIR}/meta.env" ]; then mkdir -p /usr/local/etc/vscode-dev-containers/ cp -f "${SCRIPT_DIR}/meta.env" /usr/local/etc/vscode-dev-containers/meta.env echo "${meta_info_script}" > /usr/local/bin/devcontainer-info chmod +x /usr/local/bin/devcontainer-info fi # Write marker file mkdir -p "$(dirname "${MARKER_FILE}")" echo -e "\ PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\ LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\ EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\ RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\ ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}" echo "Done!" neko-2-4-0/.devcontainer/library-scripts/docker-debian.sh000066400000000000000000000317161464615675700234170ustar00rootroot00000000000000#!/usr/bin/env bash #------------------------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. #------------------------------------------------------------------------------------------------------------- # # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md # Maintainer: The VS Code and Codespaces Teams # # Syntax: ./docker-debian.sh [enable non-root docker socket access flag] [source socket] [target socket] [non-root user] [use moby] [CLI version] ENABLE_NONROOT_DOCKER=${1:-"true"} SOURCE_SOCKET=${2:-"/var/run/docker-host.sock"} TARGET_SOCKET=${3:-"/var/run/docker.sock"} USERNAME=${4:-"automatic"} USE_MOBY=${5:-"true"} DOCKER_VERSION=${6:-"latest"} MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" DOCKER_DASH_COMPOSE_VERSION="1" set -e if [ "$(id -u)" -ne 0 ]; then echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' exit 1 fi # Determine the appropriate non-root user if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then USERNAME="" POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") for CURRENT_USER in ${POSSIBLE_USERS[@]}; do if id -u ${CURRENT_USER} > /dev/null 2>&1; then USERNAME=${CURRENT_USER} break fi done if [ "${USERNAME}" = "" ]; then USERNAME=root fi elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then USERNAME=root fi # Get central common setting get_common_setting() { if [ "${common_settings_file_loaded}" != "true" ]; then curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping." common_settings_file_loaded=true fi if [ -f "/tmp/vsdc-settings.env" ]; then local multi_line="" if [ "$2" = "true" ]; then multi_line="-z"; fi local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')" if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi fi echo "$1=${!1}" } # Function to run apt-get if needed apt_get_update_if_needed() { if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then echo "Running apt-get update..." apt-get update else echo "Skipping apt-get update." fi } # Checks if packages are installed and installs them if not check_packages() { if ! dpkg -s "$@" > /dev/null 2>&1; then apt_get_update_if_needed apt-get -y install --no-install-recommends "$@" fi } # Figure out correct version of a three part version number is not passed find_version_from_git_tags() { local variable_name=$1 local requested_version=${!variable_name} if [ "${requested_version}" = "none" ]; then return; fi local repository=$2 local prefix=${3:-"tags/v"} local separator=${4:-"."} local last_part_optional=${5:-"false"} if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then local escaped_separator=${separator//./\\.} local last_part if [ "${last_part_optional}" = "true" ]; then last_part="(${escaped_separator}[0-9]+)?" else last_part="${escaped_separator}[0-9]+" fi local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$" local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" else set +e declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" set -e fi fi if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 exit 1 fi echo "${variable_name}=${!variable_name}" } # Ensure apt is in non-interactive to avoid prompts export DEBIAN_FRONTEND=noninteractive # Install dependencies check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr if ! type git > /dev/null 2>&1; then apt_get_update_if_needed apt-get -y install git fi # Source /etc/os-release to get OS info . /etc/os-release # Fetch host/container arch. architecture="$(dpkg --print-architecture)" # Set up the necessary apt repos (either Microsoft's or Docker's) if [ "${USE_MOBY}" = "true" ]; then cli_package_name="moby-cli" # Import key safely and import Microsoft apt repo get_common_setting MICROSOFT_GPG_KEYS_URI curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list else # Name of proprietary engine package cli_package_name="docker-ce-cli" # Import key safely and import Docker apt repo curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list fi # Refresh apt lists apt-get update # Soft version matching for CLI if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then # Empty, meaning grab whatever "latest" is in apt repo cli_version_suffix="" else # Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...) docker_version_dot_escaped="${DOCKER_VERSION//./\\.}" docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}" # Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/ docker_version_regex="^(.+:)?${docker_version_dot_plus_escaped}([\\.\\+ ~:-]|$)" set +e # Don't exit if finding version fails - will handle gracefully cli_version_suffix="=$(apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")" set -e if [ -z "${cli_version_suffix}" ] || [ "${cli_version_suffix}" = "=" ]; then echo "(!) No full or partial Docker / Moby version match found for \"${DOCKER_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:" apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+' exit 1 fi echo "cli_version_suffix ${cli_version_suffix}" fi # Install Docker / Moby CLI if not already installed if type docker > /dev/null 2>&1; then echo "Docker / Moby CLI already installed." else if [ "${USE_MOBY}" = "true" ]; then apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." else apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix} fi fi # Install Docker Compose if not already installed and is on a supported architecture if type docker-compose > /dev/null 2>&1; then echo "Docker Compose already installed." else TARGET_COMPOSE_ARCH="$(uname -m)" if [ "${TARGET_COMPOSE_ARCH}" = "amd64" ]; then TARGET_COMPOSE_ARCH="x86_64" fi if [ "${TARGET_COMPOSE_ARCH}" != "x86_64" ]; then # Use pip to get a version that runns on this architecture if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then apt_get_update_if_needed apt-get -y install python3-minimal python3-pip libffi-dev python3-venv fi export PIPX_HOME=/usr/local/pipx mkdir -p ${PIPX_HOME} export PIPX_BIN_DIR=/usr/local/bin export PYTHONUSERBASE=/tmp/pip-tmp export PIP_CACHE_DIR=/tmp/pip-tmp/cache pipx_bin=pipx if ! type pipx > /dev/null 2>&1; then pip3 install --disable-pip-version-check --no-cache-dir --user pipx pipx_bin=/tmp/pip-tmp/bin/pipx fi ${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose rm -rf /tmp/pip-tmp else find_version_from_git_tags DOCKER_DASH_COMPOSE_VERSION "https://github.com/docker/compose" "tags/" echo "(*) Installing docker-compose ${DOCKER_DASH_COMPOSE_VERSION}..." curl -fsSL "https://github.com/docker/compose/releases/download/${DOCKER_DASH_COMPOSE_VERSION}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose fi fi # If init file already exists, exit if [ -f "/usr/local/share/docker-init.sh" ]; then exit 0 fi echo "docker-init doesnt exist, adding..." # By default, make the source and target sockets the same if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then touch "${SOURCE_SOCKET}" ln -s "${SOURCE_SOCKET}" "${TARGET_SOCKET}" fi # Add a stub if not adding non-root user access, user is root if [ "${ENABLE_NONROOT_DOCKER}" = "false" ] || [ "${USERNAME}" = "root" ]; then echo '/usr/bin/env bash -c "\$@"' > /usr/local/share/docker-init.sh chmod +x /usr/local/share/docker-init.sh exit 0 fi # If enabling non-root access and specified user is found, setup socat and add script chown -h "${USERNAME}":root "${TARGET_SOCKET}" if ! dpkg -s socat > /dev/null 2>&1; then apt_get_update_if_needed apt-get -y install socat fi tee /usr/local/share/docker-init.sh > /dev/null \ << EOF #!/usr/bin/env bash #------------------------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. #------------------------------------------------------------------------------------------------------------- set -e SOCAT_PATH_BASE=/tmp/vscr-docker-from-docker SOCAT_LOG=\${SOCAT_PATH_BASE}.log SOCAT_PID=\${SOCAT_PATH_BASE}.pid # Wrapper function to only use sudo if not already root sudoIf() { if [ "\$(id -u)" -ne 0 ]; then sudo "\$@" else "\$@" fi } # Log messages log() { echo -e "[\$(date)] \$@" | sudoIf tee -a \${SOCAT_LOG} > /dev/null } echo -e "\n** \$(date) **" | sudoIf tee -a \${SOCAT_LOG} > /dev/null log "Ensuring ${USERNAME} has access to ${SOURCE_SOCKET} via ${TARGET_SOCKET}" # If enabled, try to add a docker group with the right GID. If the group is root, # fall back on using socat to forward the docker socket to another unix socket so # that we can set permissions on it without affecting the host. if [ "${ENABLE_NONROOT_DOCKER}" = "true" ] && [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ] && [ "${USERNAME}" != "root" ] && [ "${USERNAME}" != "0" ]; then SOCKET_GID=\$(stat -c '%g' ${SOURCE_SOCKET}) if [ "\${SOCKET_GID}" != "0" ]; then log "Adding user to group with GID \${SOCKET_GID}." if [ "\$(cat /etc/group | grep :\${SOCKET_GID}:)" = "" ]; then sudoIf groupadd --gid \${SOCKET_GID} docker-host fi # Add user to group if not already in it if [ "\$(id ${USERNAME} | grep -E "groups.*(=|,)\${SOCKET_GID}\(")" = "" ]; then sudoIf usermod -aG \${SOCKET_GID} ${USERNAME} fi else # Enable proxy if not already running if [ ! -f "\${SOCAT_PID}" ] || ! ps -p \$(cat \${SOCAT_PID}) > /dev/null; then log "Enabling socket proxy." log "Proxying ${SOURCE_SOCKET} to ${TARGET_SOCKET} for vscode" sudoIf rm -rf ${TARGET_SOCKET} (sudoIf socat UNIX-LISTEN:${TARGET_SOCKET},fork,mode=660,user=${USERNAME} UNIX-CONNECT:${SOURCE_SOCKET} 2>&1 | sudoIf tee -a \${SOCAT_LOG} > /dev/null & echo "\$!" | sudoIf tee \${SOCAT_PID} > /dev/null) else log "Socket proxy already running." fi fi log "Success" fi # Execute whatever commands were passed in (if any). This allows us # to set this script to ENTRYPOINT while still executing the default CMD. set +e exec "\$@" EOF chmod +x /usr/local/share/docker-init.sh chown ${USERNAME}:root /usr/local/share/docker-init.sh echo "Done!"neko-2-4-0/.earthlyignore000066400000000000000000000007171464615675700153560ustar00rootroot00000000000000.git .github azure-pipelines.yml Earthfile # cmake CMakeCache.txt CMakeFiles CMakeScripts cmake_install.cmake install_manifest.txt CPackConfig.cmake CPackSourceConfig.cmake CTestTestfile.cmake DartConfiguration.tcl /cmake/uninstall.cmake # build files /build*/ /libs/src /libs/tmp /libs/download *.sln *.vcxproj *.vcxproj.filters *.opensdf *.suo Win32 Release Debug neko.sln *.dir bin /extra/chocolatey/LICENSE /extra/chocolatey/*.zip /extra/chocolatey/*.nupkgneko-2-4-0/.github/000077500000000000000000000000001464615675700140345ustar00rootroot00000000000000neko-2-4-0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001464615675700162175ustar00rootroot00000000000000neko-2-4-0/.github/ISSUE_TEMPLATE/new-issue.md000066400000000000000000000005161464615675700204620ustar00rootroot00000000000000--- name: New issue about: Neko is not actively maintained anymore. You can open an issue anyway, but don't expect to get response from developers. title: '' labels: '' assignees: '' --- *DEPRECATION NOTICE: Neko is not actively maintained anymore. You can open an issue anyway, but don't expect to get response from developers.* neko-2-4-0/.github/pull_request_template.md000066400000000000000000000001761464615675700210010ustar00rootroot00000000000000**DEPRECATION NOTICE: Neko is not actively maintained anymore. You can open a PR anyway, but don't expect it to get merged.** neko-2-4-0/.github/workflows/000077500000000000000000000000001464615675700160715ustar00rootroot00000000000000neko-2-4-0/.github/workflows/main.yml000066400000000000000000000340131464615675700175410ustar00rootroot00000000000000name: CI on: [push, pull_request] env: NEKO_VERSION: 2.4.0 jobs: linux-build: name: linux strategy: matrix: arch: [arm64, amd64] link_type: [static, dynamic] include: - link_type: static name_suffix: -static runs-on: ubuntu-20.04 steps: - name: Checkout uses: actions/checkout@v4 - name: Install Earthly run: sudo /bin/sh -c 'wget https://github.com/earthly/earthly/releases/latest/download/earthly-linux-amd64 -O /usr/local/bin/earthly && chmod +x /usr/local/bin/earthly && /usr/local/bin/earthly bootstrap --with-autocomplete' - name: Install QEMU run: | sudo apt-get install qemu binfmt-support qemu-user-static docker run --rm --privileged multiarch/qemu-user-static --reset -p yes docker stop earthly-buildkitd || true - name: Build package run: earthly --platform=linux/${{ matrix.arch }} +package --LINK_TYPE=${{ matrix.link_type }} - name: Upload binaries uses: actions/upload-artifact@v4 with: name: linux${{ matrix.name_suffix }}-${{ matrix.arch }}-binaries path: bin/${{ matrix.link_type }}/linux/${{ matrix.arch }}/neko-${{ env.NEKO_VERSION }}-linux*.tar.gz mac-build: name: mac strategy: matrix: arch: [arm64, amd64] link_type: [static, dynamic] include: - arch: arm64 os: macos-14 - arch: amd64 os: macos-12 - link_type: static BREWFILE: extra/Brewfile-STATIC_DEPS_ALL STATIC_DEPS: all name_suffix: -static - link_type: dynamic BREWFILE: extra/Brewfile-STATIC_DEPS_NONE STATIC_DEPS: none runs-on: ${{ matrix.os }} env: CMAKE_BUILD_TYPE: RelWithDebInfo steps: - name: Checkout uses: actions/checkout@v4 - name: Install Homebrew dependencies run: | set -ex brew update brew bundle --file="${{ matrix.BREWFILE }}" - name: CMake run: cmake . -DSTATIC_DEPS=${{ matrix.STATIC_DEPS }} -G Ninja -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE - name: Download dependencies run: ninja download_deps || ninja download_deps || ninja download_deps if: ${{ matrix.link_type == 'static' }} - name: Build run: ninja - name: Check runtime dependencies run: | set -ex otool -L ./bin/neko otool -L ./bin/nekoc otool -L ./bin/nekoml otool -L ./bin/nekotools - name: Test run: ctest --verbose - name: Package run: ninja package - name: Check version shell: bash run: | set -ex [ "`./bin/neko -version`" == "$NEKO_VERSION" ] - name: Upload binaries uses: actions/upload-artifact@v4 with: name: mac${{ matrix.name_suffix }}-${{ matrix.arch }}-binaries path: bin/neko-${{ env.NEKO_VERSION }}-osx*.tar.gz windows-build: name: windows strategy: matrix: arch: [Win32, x64] vstools: [vs2017, vs2019] include: - arch: x64 name_suffix: 64 - vstools: vs2017 arch: Win32 cmake_generator: Visual Studio 15 2017 - vstools: vs2017 arch: x64 cmake_generator: Visual Studio 15 2017 Win64 - vstools: vs2019 cmake_generator: Visual Studio 16 2019 env: CMAKE_BUILD_TYPE: RelWithDebInfo runs-on: windows-2019 steps: - name: Checkout uses: actions/checkout@v4 - name: Install VS2017 tools run: | choco install visualstudio2017buildtools visualstudio2017-workload-vctools if: ${{ matrix.vstools == 'vs2017' }} - name: CMake run: | cmake . -G "${{ matrix.cmake_generator }}" ${{ env.CMAKE_ARCH_FLAG }} env: CMAKE_ARCH_FLAG: ${{ matrix.vstools == 'vs2019' && format('-A {0}', matrix.arch) || ' ' }} - name: Download dependencies run: | cmake --build . --config $env:CMAKE_BUILD_TYPE --target download_deps || \ cmake --build . --config $env:CMAKE_BUILD_TYPE --target download_deps || \ cmake --build . --config $env:CMAKE_BUILD_TYPE --target download_deps - name: Build run: cmake --build . --config $env:CMAKE_BUILD_TYPE - name: Test run: ctest --verbose --build-config $env:CMAKE_BUILD_TYPE - name: Package binaries run: cmake --build . --config $env:CMAKE_BUILD_TYPE --target PACKAGE - name: Check version shell: bash run: | set -ex [ "`./bin/neko -version`" == "$NEKO_VERSION" ] - name: Upload binaries uses: actions/upload-artifact@v4 with: name: windows${{ matrix.name_suffix }}-${{ matrix.vstools }}-binaries path: bin/neko-${{ env.NEKO_VERSION }}-win${{ matrix.name_suffix }}.zip package-choco: name: chocolatey needs: [windows-build] runs-on: windows-latest defaults: run: shell: pwsh steps: - name: Checkout uses: actions/checkout@v4 - name: Download windows-vs2017-binaries uses: actions/download-artifact@v4 with: name: windows-vs2017-binaries path: windows-vs2017-binaries - name: Download windows64-vs2017-binaries uses: actions/download-artifact@v4 with: name: windows64-vs2017-binaries path: windows64-vs2017-binaries - name: Prepare Chocolatey Package run: | .\extra\chocolatey\generatePackage.ps1 $env:NEKO_VERSION Get-ChildItem extra\chocolatey\out - name: Format package version string run: | $commitSha = git rev-parse --short HEAD $commitTime = git show -s --format=%cI HEAD $commitTime = $commitTime.substring(0,19) -replace '[^0-9]','' $sourceBranch = '$(Build.SourceBranch)' $chocoVersion = If ($sourceBranch.StartsWith('refs/tags/v')) {'$env:NEKO_VERSION'} Else {"$env:NEKO_VERSION-SNAP$commitTime"} echo chocoVersion=$chocoVersion >> $env:GITHUB_ENV - name: choco pack run: choco pack --version $env:chocoVersion -Out ..\pack working-directory: extra/chocolatey/out/ - name: Upload package uses: actions/upload-artifact@v4 with: name: chocolatey path: extra/chocolatey/pack/ mac-universal: name: mac universal needs: [mac-build] runs-on: macos-latest env: INPUT_AMD64: mac-static-amd64-binaries INPUT_ARM64: mac-static-arm64-binaries steps: - name: Download ${{ env.INPUT_AMD64 }} uses: actions/download-artifact@v4 with: name: ${{ env.INPUT_AMD64 }} path: ${{ env.INPUT_AMD64 }} - name: Download ${{ env.INPUT_ARM64 }} uses: actions/download-artifact@v4 with: name: ${{ env.INPUT_ARM64 }} path: ${{ env.INPUT_ARM64 }} - name: Create universal binaries run: | tar_filename_amd64=$(basename $INPUT_AMD64/neko-*-osx64.tar.gz .tar.gz) OUTPUT=${tar_filename_amd64/osx64/osx-universal} mkdir $OUTPUT # put amd64 files straight into output to make sure all non-binary files are also included tar -xf $INPUT_AMD64/neko-*.tar.gz \ -C $OUTPUT --strip-components=1 tar -xf $INPUT_ARM64/neko-*.tar.gz \ -C $INPUT_ARM64 --strip-components=1 for filename in $OUTPUT/{*.ndll,libneko.*.*.*.dylib,neko,nekoc,nekoml,nekotools}; do filename_amd64=$filename filename_arm64=${filename/$OUTPUT/$INPUT_ARM64} lipo -create -output $filename $filename_amd64 $filename_arm64 done file $OUTPUT/* tar -czvf $OUTPUT.tar.gz $OUTPUT - name: Upload universal binaries uses: actions/upload-artifact@v4 with: name: mac-static-universal-binaries path: neko-*-osx-universal.tar.gz deploy-s3: name: deploy s3 needs: [windows-build, package-choco, mac-build, linux-build] runs-on: ubuntu-latest if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' steps: - name: Checkout uses: actions/checkout@v4 - name: Download linux-static-amd64-binaries uses: actions/download-artifact@v4 with: name: linux-static-amd64-binaries path: linux-static-amd64-binaries - name: Download linux-static-arm64-binaries uses: actions/download-artifact@v4 with: name: linux-static-arm64-binaries path: linux-static-arm64-binaries - name: Download mac-static-amd64-binaries uses: actions/download-artifact@v4 with: name: mac-static-amd64-binaries path: mac-static-amd64-binaries - name: Download mac-static-arm64-binaries uses: actions/download-artifact@v4 with: name: mac-static-arm64-binaries path: mac-static-arm64-binaries - name: Download windows-vs2017-binaries uses: actions/download-artifact@v4 with: name: windows-vs2017-binaries - name: Download windows64-vs2017-binaries uses: actions/download-artifact@v4 with: name: windows64-vs2017-binaries - name: Download chocolatey uses: actions/download-artifact@v4 with: name: chocolatey - name: Install awscli run: | set -ex sudo apt-get update -qqy sudo apt-get install -qqy awscli # https://stackoverflow.com/questions/58033366/how-to-get-current-branch-within-github-actions - name: Extract branch name id: extract_branch shell: bash run: echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT - name: Upload binaries env: AWS_ACCESS_KEY_ID: ${{ secrets.HXBUILDS_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.HXBUILDS_AWS_SECRET_ACCESS_KEY }} AWS_EC2_METADATA_DISABLED: true HXBUILDS_S3ADDR: ${{ vars.HXBUILDS_S3ADDR }} run: | set -ex COMMIT_HASH=`git rev-parse HEAD` COMMIT_HASH_SHORT=${GITHUB_SHA:0:7} COMMIT_DATE=`TZ=UTC git show --quiet --date='format-local:%Y-%m-%d' --format="%cd"` FILE_NAME=neko_${COMMIT_DATE}_${{ steps.extract_branch.outputs.branch }}_${COMMIT_HASH_SHORT} aws s3 cp linux-static-amd64-binaries/neko-*.tar.gz $HXBUILDS_S3ADDR/neko/linux64/${FILE_NAME}.tar.gz aws s3 cp linux-static-arm64-binaries/neko-*.tar.gz $HXBUILDS_S3ADDR/neko/linux-arm64/${FILE_NAME}.tar.gz aws s3 cp mac-static-amd64-binaries/neko-*.tar.gz $HXBUILDS_S3ADDR/neko/mac/${FILE_NAME}.tar.gz aws s3 cp mac-static-arm64-binaries/neko-*.tar.gz $HXBUILDS_S3ADDR/neko/mac-arm64/${FILE_NAME}.tar.gz aws s3 cp neko-*-win.zip $HXBUILDS_S3ADDR/neko/windows/${FILE_NAME}.zip aws s3 cp neko-*-win64.zip $HXBUILDS_S3ADDR/neko/windows64/${FILE_NAME}.zip aws s3 cp neko.*.nupkg $HXBUILDS_S3ADDR/neko/windows64-choco/ - name: Update "latest" if: github.ref == 'refs/heads/master' env: AWS_ACCESS_KEY_ID: ${{ secrets.HXBUILDS_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.HXBUILDS_AWS_SECRET_ACCESS_KEY }} AWS_EC2_METADATA_DISABLED: true HXBUILDS_S3ADDR: ${{ vars.HXBUILDS_S3ADDR }} run: | set -ex aws s3 cp linux-static-amd64-binaries/neko-*.tar.gz $HXBUILDS_S3ADDR/neko/linux64/neko_latest.tar.gz aws s3 cp linux-static-arm64-binaries/neko-*.tar.gz $HXBUILDS_S3ADDR/neko/linux-arm64/neko_latest.tar.gz aws s3 cp mac-static-amd64-binaries/neko-*.tar.gz $HXBUILDS_S3ADDR/neko/mac/neko_latest.tar.gz aws s3 cp mac-static-arm64-binaries/neko-*.tar.gz $HXBUILDS_S3ADDR/neko/mac-arm64/neko_latest.tar.gz aws s3 cp neko-*-win.zip $HXBUILDS_S3ADDR/neko/windows/neko_latest.zip aws s3 cp neko-*-win64.zip $HXBUILDS_S3ADDR/neko/windows64/neko_latest.zip # Chocolatey packages have to be named with version number, # so let's use web redirection to keep the original file name. [[ "$HXBUILDS_S3ADDR" =~ s3://([^/]+)(.*) ]] && HXBUILDS_S3BUCKET="${BASH_REMATCH[1]}" && HXBUILDS_S3PATH="${BASH_REMATCH[2]}" FILE_NAME=$(echo neko.*.nupkg) aws s3 cp $HXBUILDS_S3ADDR/neko/windows64-choco/${FILE_NAME} $HXBUILDS_S3ADDR/neko/windows64-choco/neko_latest.nupkg --acl public-read --website-redirect "${HXBUILDS_S3PATH}/neko/windows64-choco/${FILE_NAME}" deploy-ppa: name: deploy ppa needs: [windows-build, package-choco, mac-build, linux-build] runs-on: ubuntu-latest if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' steps: - name: Download haxeci_sec.gpg run: '# TODO' - name: Download haxeci_ssh run: '# TODO' - name: Setup credentials run: | # TODO set -ex chmod 600 $CREDENTIAL_PATH/haxeci_ssh eval `ssh-agent -s` ssh-add $CREDENTIAL_PATH/haxeci_ssh gpg --allow-secret-key-import --import $CREDENTIAL_PATH/haxeci_sec.gpg git config --global user.name "$DEBFULLNAME" git config --global user.email "$DEBEMAIL" env: CREDENTIAL_PATH: . # TODO DEBFULLNAME: ${{ secrets.DEBFULLNAME }} DEBEMAIL: ${{ secrets.DEBEMAIL }} - name: Install devscripts run: | set -ex sudo apt-get update -qqy sudo apt-get install -qqy ninja-build pkg-config libgtk-3-dev devscripts git-buildpackage ubuntu-dev-tools dh-make dh-apache2 - name: CMake run: cmake . -DSTATIC_DEPS=all - name: Upload to PPA run: make upload_to_ppa neko-2-4-0/.vscode/000077500000000000000000000000001464615675700140355ustar00rootroot00000000000000neko-2-4-0/.vscode/settings.json000066400000000000000000000000731464615675700165700ustar00rootroot00000000000000{ "files.associations": { "*.in": "cpp" } }neko-2-4-0/CHANGES000066400000000000000000000443311464615675700134740ustar00rootroot000000000000002024-07-18 : 2.4.0 all : deprecated neko (see README) std : fixed put_env when null is passed in (#229 https://github.com/HaxeFoundation/haxe/issues/10395) std : added sys_cpu_arch (#275) std : fixed sys_is64 returning false on 64 bit Windows (#276) std : fixed $int to respect whitespace and signs in hex notation and to ignore trailing text (#258) all : fixed various build issues on macOS Catalina and Big Sur cmake : update all dependencies all : added arm64 binaries for Linux and macOS, and universal binaries for macOS regexp : migrated to pcre2, fixing matches with null characters (#249) regexp : fixed stack overflow with large strings (#262) regexp : added regexp_matched_num function (#257) nekotools : allowed custom 404 handler for server (#278) nekotools : added `nekotools boot -c *.n` option to help output (#273) ui : migrated to gtk3 on Linux (#220) sqlite : fixed early finalizer call (#200) cmake : fixed loading error when installing to /usr/local on Unix (#271) gc : fixed "GetThreadContext failed" error on Windows (#264) std : fixed date_format crash on windows (#279) vm : added more detailed errors for failed stack checks (#284) ssl : migrated to mbedtls 3 (#290) ssl : ignored MBEDTLS_ERR_SSL_WANT_READ and MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET (#291) 2019-10-19 : 2.3.0 std : added socket_set_broadcast function (#190) std : fixed sha1_update call (#194) std : fixed 0x7F detection in utf8 functions std : changed date_get_tz to take an argument (a timestamp), and to return minutes instead of seconds (#195) std : added date_utc_format, date_get_utc_day, and date_get_utc_hours - all variants of existing functions, but using gmtime_r instead of localtime_r (#195) 2017-12-19 : 2.2.0 nekotools : added `nekotools boot -c *.n`, which generates a C file that contains the input Neko bytecode (#130) nekotools : fixed `nekotools boot` not able to find neko when a value in PATH does not have a trailing slash (#148) cmake : recognize common install location variables by using GNUInstallDirs cmake : fixed various build issues, particularly for FreeBSD (Thanks, ppenzin!), and cygwin cmake : added NekoConfig.cmake to ease building ndll or native program using Neko with CMake cmake : replaces WITH_NDLLS with WITH_* (#157) cmake : does not recompile nekoc/nekoml unless RECOMPILE_NEKOC_NEKOML (#171) all : added NEKO_MODULE_PATH in neko.h that points to the installation path of the ndll files std : bugfix in utf8_compare (https://github.com/HaxeFoundation/haxe/issues/5308) ssl : handle MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY (#150) ssl : allow partial failure on loading certificates (#160) mysql : use eof terminated string when appropriated (#159) jit : disable jit for win64, which never worked all : introduced NEKO_BIG_ENDIAN and NEKO_LITTLE_ENDIAN to replace IS_BIG_ENDIAN, IS_LITTLE_ENDIAN, LITTLE_ENDIAN, BIG_ENDIAN, and BYTE_ORDER all : renamed TAG_BITS to NEKO_TAG_B1ITS all : add NEKO_JIT_DISABLE and NEKO_JIT_DEBUG CMake options all : VS2015 and 2017 compatiblity (#133 #172) sqlite : fixed reading 32-bit int from sqlite (#167) 2016-05-08 : 2.1.0 all : switched to use CMake for building (#122) mod_neko/tora : use remote_ip (fixes Apache 2.2 returning 0.0.0.0 in 64 bits ?) std : added buffer_get_length std : added socket_send_to and socket_recv_from for UDP, added socket_set_keepalive all : increased max codesize from 65K opcodes to 1M nekoc : allow break/continue in try/catch std : bugfix in utf8_compare vm : added $sget16/$sget32/$sgetf/$sgetd, $sset16/$sset32/$ssetf/$ssetd, and $itof/$itod/$ftoi/$dtoi vm : bugfix with arrays/strings having length >128MB jit : bugfix on some GCC versions causing invalid array access std : fixed escaping of arguments in process_run all : nekotools boot executable can now be safely striped on Linux (#86) all : nekotools boot now looks for the Neko VM binary in cwd, $loader.path, and PATH in order (#110) std : added epoll support (#67) all : libneko can now be loaded from current directory all : libneko is now versioned vm : comparisons with NaN and Int/Float now always returns false api : added kind_lookup (#6) mod_neko/tora : distribute ndlls compiled against Apache 2.4 (can still manually compile against Apache 2.2) mysql : use MariaDB's LGPL connector/c instead of MySQL's GPL connector/c (can still use MySQL for compilation) all : copyright to Haxe Foundation instead of Nicolas and other license clean up (#102) 2013-02-23 : 2.0.0 all : changed license to MIT mod_neko+mod_tora : fixed issue with get_client_ip in 64 bits mode /gcc mysql : added support for timestamp to date conversion std : added merge_sort native implementation std : added mem_local_size regexp : fix in regexp_match, ^ no longer match if pos > 0 tools : don't abort server on invalid http request std : fixed sys_command error code (was <<8 on Linux/OSX) nekoc : added -version which adds Loop opcode and ensure correct $array evaluation order vm : support for bytecode version and new Loop/MakeArray2 opcodes mod_neko/tora : only mix POST with GET params if content-type contains 'urlencoded' mysql5 : added charset support check for escape() nekoc : fixed bug with switch tables where max >= 256 neko : int32 is now a first class value std : md5 and hashing now takes the int32 value into account neko : max array/string size is now 256MB instead of 512MB std : date_new("YYYY-MM-DD") now uses localtime instead of UTC (same as Y-M-D H:M:S) neko : fixed missing stack for array write in interp mode with invalid index neko : added "-version" to print version and exit 2011-09-10 : 1.8.2 vm : use 15 digits (instead of 10) for float display std : allow up to 512MB array/string size in serialize (instead of 1MB) std : bugfix in utf8 (with 4 bytes codes) vm : finalizers bugfix on Windows std : added sys_thread_cpu_time() vm : use VEXTERN for neko_kind_module and neko_id_module std : hide process console in process_run nekoml : added Math core module std : fixed timeout error for sockets on Windows mod_tora : added PROXY_MODE configuration std : fixed timeout error for sockets on Linux vm : fixed $int and $float - return null on invalid strings mysql : added result_get_fields_names gc : prevent issues with AV softs / GoogleDesktop which inject threads vm : use sse2 for fp calculus on gcc (double precision, same as msvc/flash) removed in updated build (some CPU are not compatible) mysql : added support for BINARY(size) type vm : keep stack infos for object/array ops (interp) std : added thread_stack vm : added $fasthash mysql5 : fixed 5.5 protocol change std : fixed sha1 for 64-bits (also fix mysql5 auth) std : added make_sha1 mysql : added set_conv_funs for string + bytes wrapping support 2009-07-26 : 1.8.1 nekoc : small fix in evaluation of using jump tables for switch on integers regexp : use a recursion limit to prevent stack overflows in pcre mysql5 : completely rewrote mysql client mod_tora : complete rearchitecture with abstract protocol std : added socket_set_fast_send mod_tora : use socket fast send to optimize timing mod_tora : fixed bug when .n file does not exists mod_tora : fix for Apache 2.x - does not set the content-type if declined mod_tora : added port range configuration (for random tora server) std, mod_tora : propagate jit mode to created threads nekoc : optimization for debug infos : precompute file table at compile-time threads : added neko_gc_register_thread (but buggy, so not used) mysql5 : added 5 hours network timeout while waiting for an answer mysql5 : fixed some additional errors handling mysql5 : allow big requests (more than 16MB) vm : optimize object table - one word and one memory block saved per object jit : one more crash fix for the + operation and unexisting overloaded operator nekoml : added Sys.is_directory, Sys.read_directory and Regexp.split nekoml : don't allow empty match vm : create pthreads in detached mode (prevent leak on osx/linux) std : added math_fceil,math_ffloor,math_fround,math_int,process_kill nekoc : forbidden usage of 'var' outside blocks 2008-09-23 : 1.8.0 added mod_tora (neko application server) changed mod_neko get_host_name : return the http host instead of the server IP bugfix in nekoml : fixed usage of nekoml.std + added -nostd std : added EINTR handling in send/recv socket operations and fread/fwrite gc : upgraded windows version to 7.1 vm : fixed comparison of bool vm : moved threads stuff into threads.c nekoml : fixed lexing engine, allow escape sequences in char groups mysql : link with thread_safe version on linux std : fixed threads issues with host_resolve and host_reverse libs : compile with -pthread on linux/osx to ensure thread safety main : turned ON jit by default for bootable executables std : added set_trusted primitive gc : use alloc_ignore_off_page for large blocks gc : use finalizers with no_order (prevent cycles) mysql : prevent hashing the fields that looks like inner requests vm : optimized global fields cache std : string_split doesn't copy the string anymore if the pattern is not found vm : added neko_vm_dump_stack std : use per-thread locale on POSIX systems std : url_encode does not encode anymore - and . chars std : fixed sleep() for unix (more accurate and deal with signals) std : added module_read_string and module_set_name std : fixed some file descriptor issues with processes and threads jit : fixed crash whith unsupported OO overloading vm : added $getkind and $iskind builtins std : added same_closure for closure comparison 2008-07-28 : 1.7.1 nekotools : fixed set_return_code in web server std : display windowed application in process_run mod_neko : allowed to uncache module with set_main(null) mod_neko : use strmcpi for set_header("Content-Type") nekoc : one last fix for big arrays (first element was not compiled) mod_neko : added get_http_method nekoml : minor fixes in lexer nekoml : added -pack and -use, auto build nekoml.std include nekoml as part of standard distribution vm/security : $smake now fills the string with 0 std/int32 : fixed need_32_bits macro (negative values) bugfix : >> and >>> were reversed in JIT bugfix : date functions are now MT-safe removed context.h and context.c added lock and tls multithread API to neko.h std : added date_get_tz() mod_neko : added log_message regexp : fix for unmatched optional groups, return null instead of empty string threads : for windows, use critical_section instead of mutex for locks threads : for posix, use a recursive mutex which match windows behavior std : added mutex and deque api (threads) 2008-03-16 : 1.7.0 nekoc : allow binary ast format with -p, don't allow inline nxml std : allow int32_new float parameter vm : reduce debug infos memory size by 16 vm : fixed $int in case of overflow regexp : upgraded windows ndll to use pcre 7.3 with utf8 support fixed : math_round(0.5) is now 1 fixed : date_set_hour and date_set_day crash with invalid dates fixed : bug in vm/callback.c when stack address is >2GB sockets : added support for socket_poll for Windows sockets : added socket_poll_prepare and socket_poll_events thread : added tls_create, tls_set, tls_get vm : threaded interpreter (gcc only) vm : always use COMPACT_TABLE for objects mod_neko : log some errors into apache log neko+vm : added perf statistics hooks (-stats) mod_neko : scriptable configuration and statistics regexp : allow more than 10 matches std : added process_close vm : added $aconcat nekoc : fixed big arrays declarations std : added sys_is64 2007-07-25 : 1.6.0 display error message when uncaught exception in neko thread fix bug in long run JIT programs : no C functions callable anymore use a smarter way to prevent C stack overflow nekoc : stack align error message set max-stack-per-function to 128 and default-stack-size to 256 jit now check stack overflow on function entry and not on every push mod_neko2 set MOD_NEKO=2 env var (for version detection) mod_neko2 : remove Apache 2.0 error message, use 302 http code for redirect fixed fallback of interp operator overloading fix for 64-bit CPU minor optimization for [0] jit array access gnuk/freebsd support fixed thread messages on Windows (do not use system queue) thread_current returns unique value remove kind_import, kind_export, added kind_share added ui library added libs/std/process api functions new binary ast format (faster than nxml) 2007-01-28 : 1.5.3 minor fixes in mod_neko multipart and POST data handling fixed $objremove result (was always true) fixed one-last-bug when using more-than-five-arguments method call return value fixed bug in dev webserver when accessing a directory added xcross support fixed bug with $setresolver in bytecode interpreter changed bootable vm implementation (easier, allow compressed bin) added .mode in sys_stat change in $version format 2006-11-22 : 1.5.2 fixed std math_pow float minor thread cleanup added MySQL5.ndll for Windows fields hash cache is now global (instead of per-thread) socket_select retry on EINTR fixed under-second lock timeout for Linux/OSX added socket_poll for Linux/OSX (emulate with select() on Windows) fixed compilation bug when accessing 'this' in arguments and more than 5 args fixed string_split with empty string (was causing infinite loop) fixes in more-than-five-arguments-calls added $varargs 2006-10-29 : 1.5 neko web server : get_client_header is now case-insensitive std : new thread and lock api neko : when run from commandline, SEGV on Linux are turned into exceptions + added mod_neko2 error if used with Apache 2.0.x 2006-10-09 : 1.4.5 sqlite : added BOOL handling jit : added mmap support (for execution protection) jit : fixed bug when unsupported operation exception vm : fixed bug in debug infos reading when nfiles > 255 vm : fixed bug in interp -> jit call vm : default NEKOPATH is now the same on OSX/Linux vm : optimized debug infos runtime memory size std : added misc.c (float & double bytes manipulations + gc/vm functions) std : fixed buf in sys_read_dir on windows (directory not closed correctly) std : added sys_getch, sys_get_pid std : fixed socket_host (was returning the same as socket_peer) std : improved performances of serialize/unserialize allowed break & continues in try/catch if they don't break outside nekoml : fixed bug in lexer allocating too bug memory when small reads nekotools : bugfixes in server, added static file streaming nekotools : fixed boot for universal binaries mod_neko : minor updates for better memory handling mod_neko : use soft timeout for Apache 1.3 2006-08-02 : 1.4 std : added a SO_REUSEADDR before socket_bind (except windows) nekoc/nekovm : allowed more than 127 filenames in debug infos nekotools server : added -rewrite for mod_rewrite'style urls added zlib wrapper mod_neko for apache 2.0 added sqlite wrapper std : fixed socket_set_timeout (changed unit from millisec to seconds) std : fixed math_pow (integer overflow) , fixed readdir (missing closedir on Linux) added PPC registers neko.h : added NEKO_* OS/CPU flags added JIT for x86 vm : (int mod 0) is now an exception (and not NaN) neko.h : added kind_import and kind_export to share abstracts between libraries std : handle signals in socket send and recv allowed OSX universal build added $setresolver for unknown object field handling 2006-05-11 : 1.3 neko : fixed endianness bug needed to boot, exported neko_is_big_endian neko : fixed bug on Linux x86 gcc with object operators (eax get overwritten) neko : fixed bug : error when __string or __compare does not exist neko : allowed runtime printer redirection (+ added std@print_redirect) neko : defined a default NEKOPATH when not specified (less configuration) neko : fixed int32 calculus bug (need_32_bits macro) neko : fixed calls with a lot of arguments (some cases were failing) neko : fixed preservation of 'this' through tailcall, if changed inside the call neko : added unary operator minus parsing neko : fixed error reporting of unclosed parenthesis nekoml : added Net core module for sockets nekoml : when catching Neko errors, wrap them using Neko_error constructor std : supported serialization of C primitives and __serialize/__unserialize overrides added nekotools (merged nekoboot and neko webserver) std : fixed math_round, fixed math docs std : fixed bug in utf8_resize, utf8_get and utf8_iter. regexp : added regexp_new_options for matching options and regexp_replace_fun regexp : fixed invalid matched length when not index 0 added some benchmarks in src/benchs neko : fixed $ablit and $sblit (when used with same array/string) neko : fixed multithread win32 support mod_neko : changed Content-Type handling for POST data std : added blocking sockets support std : changed sys_time to sys_cpu_time, added sys_time (local time). std : fixed put_env on Linux (GC issue) cleanup of some stuff needed by experimental JIT std : added memory module 2006-01-09 : 1.2 nekoc: added linker : provide linked versions of neko & nekoml compilers added tail calls optimization nekoml: little improved nekoml->neko patterns generator libs: added md5 digest , utf8 api and base_encode/base_decode in std some changes in mod_neko api mysql api now threat tinyint(1) as booleans improved xml parser : support for doctype some fixes for OSX and C++ compilation neko: renamed 'nekovm' to 'neko' experimental jit support object prototypes moved args from builtin ($args) to loader ($loader.args) makeboot can create standalone binaries runtime errors on : - calls with invalid number of arguments - field accesses of not-an-object - binary operations on invalid types - array accesses on not-an-array and not-an-object license change : from GPL to LGPL 2005-11-10 : 1.1 vm : ports PPC (big endian) and AMD64 added stack conservation check at bytecode loadtime : faster VM runtime some more opcodes for better speed added exception and call stack traces added debug informations for bytecode added dispatch tables (for integer switchs) tuned GC usage reorganized VM apis neko: added labels in the language specification added $goto and $apply added switchs added documentation generator nekoml: added NekoML bootstrapped the language (no more need for ocaml) libs: rewrote and completed standard library added generated documentation 2005-08-17 : 1.0 compiler in ocaml virtual machine mod_neko some small libraries neko-2-4-0/CMakeLists.txt000066400000000000000000000564031464615675700152440ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10.2) if (${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.24) cmake_policy(VERSION 3.24) elseif (${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.19) cmake_policy(VERSION 3.19) endif() project(Neko C) include(GNUInstallDirs) include(CheckCCompilerFlag) include(CheckIncludeFile) include(TestBigEndian) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") # FreeBSD puts all thirdparty libraries in /usr/local link_directories(/usr/local/lib) endif() # put output in "bin" set(OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR}) # avoid the extra "Debug", "Release" directories # https://stackoverflow.com/questions/7747857/in-cmake-how-do-i-work-around-the-debug-and-release-directories-visual-studio-2 foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${OUTPUT_DIR} ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${OUTPUT_DIR} ) set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${OUTPUT_DIR} ) endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) # Make sure CMAKE_INSTALL_LIBDIR is relative if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR}) file(RELATIVE_PATH CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_LIBDIR}) endif() set(NEKO_VERSION_MAJOR 2) set(NEKO_VERSION_MINOR 4) set(NEKO_VERSION_PATCH 0) string(TIMESTAMP NEKO_BUILD_YEAR "%Y") # NEKO_VERSION is cached such that we can query it by `cmake -L -N -B . | grep NEKO_VERSION` set(NEKO_VERSION ${NEKO_VERSION_MAJOR}.${NEKO_VERSION_MINOR}.${NEKO_VERSION_PATCH} CACHE STRING INTERNAL) # Determine target endianness TEST_BIG_ENDIAN(NEKO_BIG_ENDIAN) option(WITH_REGEXP "Build with Perl-compatible regex support." ON) option(WITH_UI "Build with GTK-3 UI support." ON) option(WITH_SSL "Build with SSL support." ON) option(WITH_MYSQL "Build with MySQL support." ON) option(WITH_SQLITE "Build with SQLite support." ON) option(WITH_APACHE "Build with Apache modules." ON) option(WITH_NEKOML "Build NekoML." ON) # Process common headers in libraries # TODO libraries should not be built from this file, but rather by traversing the tree using add_subdirectory #add_subdirectory(libs) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(arch_64 "64") else() set(arch_64 "") endif() if(WIN32) if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set (CMAKE_INSTALL_PREFIX "C:/HaxeToolkit/neko" CACHE PATH "default install path" FORCE) endif() set(NEKO_MODULE_PATH ${CMAKE_INSTALL_PREFIX}) else() set(NEKO_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/neko) endif() if(APPLE AND STATIC_DEPS STREQUAL "all") if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "CMAKE_OSX_DEPLOYMENT_TARGET" FORCE) endif() endif() check_include_file(xlocale.h NEKO_XLOCALE_H) option(NEKO_JIT_DISABLE "Disable Neko JIT." OFF) option(NEKO_JIT_DEBUG "Debug Neko JIT." OFF) configure_file ( "${CMAKE_SOURCE_DIR}/vm/neko.h.in" "${CMAKE_BINARY_DIR}/neko.h" ) set(external_deps BoehmGC Zlib OpenSSL MariaDBConnector pcre2 SQLite3 APR APRutil Apache MbedTLS ) if (WIN32) set(STATIC_DEPS_DEFAULT "all") else() set(STATIC_DEPS_DEFAULT "none") option(RELOCATABLE "Set RPATH to $ORIGIN (Linux) / @executable_path (Mac)." ON) if (NOT APPLE) option(RUN_LDCONFIG "Run ldconfig after install." ON) endif() endif() set(STATIC_DEPS_DOC "Dependencies that should be linked statically. Can be \"all\", \"none\", or a list of library names (e.g. \"${external_deps}\").") set(STATIC_DEPS ${STATIC_DEPS_DEFAULT} CACHE STRING "${STATIC_DEPS_DOC}") # Validate STATIC_DEPS if (STATIC_DEPS STREQUAL "all") set(STATIC_DEPS_ALL ${external_deps}) if (WIN32) list(REMOVE_ITEM STATIC_DEPS_ALL BoehmGC) endif() set(STATIC_DEPS ${STATIC_DEPS_ALL} CACHE STRING "${STATIC_DEPS_DOC}" FORCE) elseif (STATIC_DEPS STREQUAL "none") message(STATUS "set STATIC_DEPS to nothing") set(STATIC_DEPS CACHE STRING "${STATIC_DEPS_DOC}" FORCE) else() foreach(dep ${STATIC_DEPS}) list(FIND external_deps ${dep} idx) if(idx EQUAL -1) message(FATAL_ERROR "Invalid STATIC_DEPS. There is no ${dep} in the list of ${external_deps}") elseif(WIN32 AND dep STREQUAL "BoehmGC") message(FATAL_ERROR "Cannot link ${dep} statically on Windows") endif() endforeach() endif() # Set STATIC_* variables according to STATIC_DEPS. foreach(dep ${external_deps}) string(TOUPPER ${dep} var) list(FIND STATIC_DEPS ${dep} static_idx) if (static_idx EQUAL -1) set(STATIC_${var} FALSE) else() set(STATIC_${var} TRUE) endif() endforeach() include(ExternalProject) if (RELOCATABLE) # https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling # Set this to true, otherwise the binaries won't be relocatable until after installation: set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) if(APPLE) set(CMAKE_INSTALL_RPATH @executable_path/) elseif(UNIX) set(CMAKE_INSTALL_RPATH \$ORIGIN) endif() endif() list(APPEND CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}) if(UNIX AND NOT APPLE) add_definitions(-DABI_ELF) endif() if(UNIX) add_definitions(-D_GNU_SOURCE) add_compile_options(-fno-omit-frame-pointer) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) set(ARG_PIC -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE) # https://github.com/HaxeFoundation/neko/pull/17 if(CMAKE_SIZEOF_VOID_P EQUAL 4) check_c_compiler_flag(-mincoming-stack-boundary=2 HAS_MINCOMING_STACK_BOUNDARY) check_c_compiler_flag(-mstack-alignment=2 HAS_MSTACK_ALIGNMENT) if(HAS_MINCOMING_STACK_BOUNDARY) add_compile_options(-mincoming-stack-boundary=2) elseif(HAS_MSTACK_ALIGNMENT) add_compile_options(-mstack-alignment=2) endif() endif() find_package(PkgConfig REQUIRED) endif() # git is used for source_archive and for applying patch find_package(Git REQUIRED) # copy the lib/src folder to build directory # (if it is a fat archive, there will be external libraries) if(EXISTS ${CMAKE_SOURCE_DIR}/libs/src) file(COPY ${CMAKE_SOURCE_DIR}/libs/src DESTINATION ${CMAKE_BINARY_DIR}/libs) endif() # ExternalProject configs set(EP_CONFIGS PREFIX ${CMAKE_BINARY_DIR}/libs DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/libs/download ) list(APPEND EP_CONFIGS DOWNLOAD_NO_PROGRESS 1 ) if (${CMAKE_VERSION} VERSION_LESS 3.19) list(APPEND EP_CONFIGS INDEPENDENT_STEP_TARGETS download ) else() # download is independent by default in 3.19 list(APPEND EP_CONFIGS STEP_TARGETS download ) endif() set(EP_PROPS EXCLUDE_FROM_ALL 1 ) include_directories( ${CMAKE_BINARY_DIR} vm libs/common ) file(GLOB libneko_public_headers vm/neko*.h ) list(APPEND libneko_public_headers ${CMAKE_BINARY_DIR}/neko.h ) add_library(libneko SHARED vm/alloc.c vm/builtins.c vm/callback.c vm/elf.c vm/interp.c vm/load.c vm/objtable.c vm/others.c vm/hash.c vm/module.c vm/jit_x86.c vm/threads.c ) add_executable(nekovm vm/stats.c vm/main.c ) # We need to build from source on Windows regardless if (STATIC_BOEHMGC OR WIN32) ExternalProject_Add(libatomic_ops ${EP_CONFIGS} URL https://github.com/ivmai/libatomic_ops/releases/download/v7.6.14/libatomic_ops-7.6.14.tar.gz URL_MD5 ee8251f5091b7938d18be4dda843a515 CONFIGURE_COMMAND echo skip config BUILD_COMMAND echo skip build INSTALL_COMMAND echo skip install ) set_target_properties(libatomic_ops PROPERTIES ${EP_PROPS}) set ( BoehmGC_CONFIGS DEPENDS libatomic_ops URL https://github.com/ivmai/bdwgc/releases/download/v7.6.16/gc-7.6.16.tar.gz URL_MD5 74fb76b6bccf0874cec65b794535a7dd ) set(GC_INCLUDE_DIR ${CMAKE_BINARY_DIR}/libs/src/BoehmGC-build/include) if (WIN32) set(GC_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/BoehmGC-build/${CMAKE_CFG_INTDIR}/gcmt-dll.lib ) ExternalProject_Add(BoehmGC ${EP_CONFIGS} ${BoehmGC_CONFIGS} CMAKE_ARGS -Wno-dev -Denable_threads=ON -Denable_parallel_mark=OFF -DCMAKE_USE_WIN32_THREADS_INIT=ON -DCMAKE_CXX_STANDARD=14 PATCH_COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/libs/src/libatomic_ops ${CMAKE_BINARY_DIR}/libs/src/BoehmGC/libatomic_ops INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/libs/src/BoehmGC/include ${CMAKE_BINARY_DIR}/libs/src/BoehmGC-build/include/gc ) add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/gcmt-dll.dll DEPENDS BoehmGC COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/libs/src/BoehmGC-build/${CMAKE_CFG_INTDIR}/gcmt-dll.dll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) add_custom_target(gcmt-dll.dll ALL DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/gcmt-dll.dll ) add_dependencies(nekovm gcmt-dll.dll) else() if (APPLE) set(GC_CFLAGS "-w -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") else() set(GC_CFLAGS "-w") endif() set(GC_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/BoehmGC-build/lib/libgc.a ) ExternalProject_Add(BoehmGC ${EP_CONFIGS} ${BoehmGC_CONFIGS} PATCH_COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/libs/src/libatomic_ops ${CMAKE_BINARY_DIR}/libs/src/BoehmGC/libatomic_ops CONFIGURE_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/BoehmGC && ./configure --prefix=${CMAKE_BINARY_DIR}/libs/src/BoehmGC-build --enable-threads=posix --with-pic --enable-shared=no --enable-static=yes --enable-silent-rules --silent BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/BoehmGC && make "CFLAGS=${GC_CFLAGS}" INSTALL_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/BoehmGC && make install BYPRODUCTS ${GC_LIBRARIES} ) endif() set_target_properties(BoehmGC PROPERTIES ${EP_PROPS}) add_dependencies(libneko BoehmGC) else() find_package(BoehmGC REQUIRED) endif() add_custom_target(download_deps) if (STATIC_BOEHMGC OR WIN32) add_dependencies(download_deps libatomic_ops-download) add_dependencies(download_deps BoehmGC-download) endif() target_include_directories(libneko PRIVATE ${GC_INCLUDE_DIR}) target_link_libraries(libneko ${GC_LIBRARIES}) target_link_libraries(nekovm libneko) if(UNIX) if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") set(DL_LIB "dl") endif() find_package(Threads) target_link_libraries(libneko ${DL_LIB} m ${CMAKE_THREAD_LIBS_INIT}) endif() set_target_properties(nekovm libneko PROPERTIES OUTPUT_NAME neko ) set_target_properties(libneko PROPERTIES VERSION ${NEKO_VERSION} SOVERSION ${NEKO_VERSION_MAJOR} COMPILE_DEFINITIONS "_USRDLL;NEKOVM_DLL_EXPORTS;NEKO_SOURCES" PUBLIC_HEADER "${libneko_public_headers}" PDB_NAME libneko ) ####################### # compilers # nekoc, nekoml, nekotools, and test.n if (CMAKE_HOST_WIN32) set(set_neko_env set NEKOPATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) set(neko_exec $) else() set(set_neko_env "") set(neko_exec NEKOPATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY} $) endif() file(GLOB compilers_src src/neko/*.nml src/nekoml/*.nml boot/*.n ) if (RECOMPILE_NEKOC_NEKOML) add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n COMMAND ${set_neko_env} COMMAND ${neko_exec} ../boot/nekoml.n -nostd neko/Main.nml nekoml/Main.nml COMMAND ${neko_exec} ../boot/nekoc.n -link ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n neko/Main COMMAND ${neko_exec} ../boot/nekoc.n -link ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n nekoml/Main VERBATIM DEPENDS nekovm std.ndll ${compilers_src} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src ) else() add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n COMMAND ${CMAKE_COMMAND} -E copy ../boot/nekoc.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${CMAKE_COMMAND} -E copy ../boot/nekoml.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} DEPENDS std.ndll VERBATIM WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src ) endif() add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test.n COMMAND ${set_neko_env} COMMAND ${neko_exec} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n tools/test.neko COMMAND ${CMAKE_COMMAND} -E copy tools/test.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${CMAKE_COMMAND} -E remove tools/test.n VERBATIM DEPENDS nekovm std.ndll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n ${CMAKE_SOURCE_DIR}/src/tools/test.neko WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src ) add_custom_target(test.n ALL DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test.n) add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoboot.n COMMAND ${set_neko_env} COMMAND ${neko_exec} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n tools/nekoboot.neko COMMAND ${CMAKE_COMMAND} -E copy tools/nekoboot.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} # COMMAND ${CMAKE_COMMAND} -E remove tools/nekoboot.n VERBATIM DEPENDS nekovm std.ndll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n ${CMAKE_SOURCE_DIR}/src/tools/nekoboot.neko WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src ) file(GLOB nekotools_src src/tools/*.nml ) add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekotools.n COMMAND ${set_neko_env} COMMAND ${neko_exec} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n -nostd -p tools Tools.nml COMMAND ${neko_exec} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n -link ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekotools.n Tools VERBATIM DEPENDS nekovm std.ndll ${nekotools_src} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoboot.n WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src ) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/nekoc.c COMMAND ${set_neko_env} COMMAND ${neko_exec} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoboot -c ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.c ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.c VERBATIM DEPENDS nekovm std.ndll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoboot.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoc.n WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) add_executable(nekoc ${CMAKE_BINARY_DIR}/nekoc.c) target_link_libraries(nekoc libneko) if (WITH_NEKOML) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/nekoml.c COMMAND ${set_neko_env} COMMAND ${neko_exec} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoboot -c ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.c ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.c VERBATIM DEPENDS nekovm std.ndll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoboot.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) add_executable(nekoml ${CMAKE_BINARY_DIR}/nekoml.c) target_link_libraries(nekoml libneko) endif() add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/nekotools.c COMMAND ${set_neko_env} COMMAND ${neko_exec} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoboot -c ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekotools.n COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekotools.c ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekotools.c VERBATIM DEPENDS nekovm std.ndll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoboot.n ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekotools.n WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) add_executable(nekotools ${CMAKE_BINARY_DIR}/nekotools.c) target_link_libraries(nekotools libneko) if (WITH_NEKOML) file(GLOB CORE_NMLS RELATIVE ${CMAKE_SOURCE_DIR}/src src/core/*.nml) set(nekoml_std ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.std) add_custom_command(OUTPUT ${nekoml_std} COMMAND ${set_neko_env} COMMAND ${neko_exec} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n -nostd neko/Main.nml nekoml/Main.nml ${CORE_NMLS} -pack ${nekoml_std} VERBATIM DEPENDS zlib.ndll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.n WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src ) add_custom_target(nekoml.std ALL DEPENDS ${nekoml_std}) endif() ####################### # source_archive # We create our own source package target instead of using CPack's package_source. # One reason is that the CPack VS generator doesn't generate package_source target. # See https://cmake.org/Bug/view.php?id=13058 if (WIN32) set(source_archive_format zip) else() set(source_archive_format tar.gz) endif() set(source_archive_name_we neko-${NEKO_VERSION}-src) set(source_archive_name ${source_archive_name_we}.${source_archive_format}) add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_archive_name} COMMAND ${GIT_EXECUTABLE} archive --prefix=${source_archive_name_we}/ -o ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_archive_name} HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} VERBATIM ) add_custom_target(source_archive DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_archive_name} ) # source_archive_fat # It is source_archive + STATIC_DEPS placed in libs/src. set(source_archive_fat_name_we neko-${NEKO_VERSION}-src-fat) set(source_archive_fat_name ${source_archive_fat_name_we}.${source_archive_format}) add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_archive_fat_name} COMMAND ${CMAKE_COMMAND} -Dsource_archive_name_we=${source_archive_name_we} -Dsource_archive=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_archive_name} -Dsource_archive_fat_name_we=${source_archive_fat_name_we} -Dsource_archive_fat_name=${source_archive_fat_name} -Dbin_dir=${CMAKE_BINARY_DIR} -Dsrc_dir=${CMAKE_SOURCE_DIR} -Dlib_src_dir=libs/src -P ${CMAKE_SOURCE_DIR}/cmake/source_archive_fat.cmake DEPENDS source_archive download_deps VERBATIM ) add_custom_target(source_archive_fat DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_archive_fat_name} ) ####################### # install target if (WIN32) set(DEST_BIN .) set(DEST_LIB .) set(DEST_NDLL .) set(DEST_INCLUDE "include") set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .) include(InstallRequiredSystemLibraries) install ( FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/gcmt-dll.dll DESTINATION . ) else() set(DEST_BIN ${CMAKE_INSTALL_BINDIR}) set(DEST_LIB ${CMAKE_INSTALL_LIBDIR}) set(DEST_NDLL ${CMAKE_INSTALL_LIBDIR}/neko) # should match NEKO_MODULE_PATH set(DEST_INCLUDE ${CMAKE_INSTALL_INCLUDEDIR}) endif() install ( TARGETS nekovm nekoc nekotools libneko EXPORT NekoTargets RUNTIME DESTINATION ${DEST_BIN} LIBRARY DESTINATION ${DEST_LIB} ARCHIVE DESTINATION ${DEST_LIB} PUBLIC_HEADER DESTINATION ${DEST_INCLUDE} ) if (WITH_NEKOML) install ( TARGETS nekoml EXPORT NekoTargets RUNTIME DESTINATION ${DEST_BIN} LIBRARY DESTINATION ${DEST_LIB} ARCHIVE DESTINATION ${DEST_LIB} PUBLIC_HEADER DESTINATION ${DEST_INCLUDE} ) install ( FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nekoml.std DESTINATION ${DEST_NDLL} ) endif() include(CMakePackageConfigHelpers) if(WIN32 AND NOT CYGWIN) set(DEF_INSTALL_CMAKE_DIR CMake) else() set(DEF_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/Neko) endif() set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") export(TARGETS nekovm nekoc nekotools libneko FILE "${CMAKE_BINARY_DIR}/NekoTargets.cmake") if (WITH_NEKOML) export(TARGETS nekoml APPEND FILE "${CMAKE_BINARY_DIR}/NekoTargets.cmake") endif() export(PACKAGE Neko) # NekoConfig referencing build dir set(NEKO_INCLUDE_DIRS "${CMAKE_BINARY_DIR};${CMAKE_SOURCE_DIR}/vm") set(NEKO_TARGETS_FILE "${CMAKE_BINARY_DIR}/NekoTargets.cmake") configure_package_config_file( cmake/NekoConfig.cmake.in "${CMAKE_BINARY_DIR}/NekoConfig.cmake" INSTALL_DESTINATION ${CMAKE_BINARY_DIR} PATH_VARS NEKO_INCLUDE_DIRS NEKO_TARGETS_FILE ) # NekoConfig referencing install dirs set(NEKO_INCLUDE_DIRS "${DEST_INCLUDE}") set(NEKO_TARGETS_FILE "${INSTALL_CMAKE_DIR}/NekoTargets.cmake") configure_package_config_file( cmake/NekoConfig.cmake.in "${OUTPUT_DIR}/NekoConfig.cmake" INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}" PATH_VARS NEKO_INCLUDE_DIRS NEKO_TARGETS_FILE ) write_basic_package_version_file( NekoConfigVersion.cmake VERSION ${NEKO_VERSION} COMPATIBILITY SameMajorVersion ) install(FILES ${OUTPUT_DIR}/NekoConfig.cmake ${CMAKE_BINARY_DIR}/NekoConfigVersion.cmake DESTINATION ${INSTALL_CMAKE_DIR} COMPONENT dev ) install( EXPORT NekoTargets DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev ) if (RUN_LDCONFIG) install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/ldconfig.cmake") endif() # A script to create a flat installation for archive package set (NEKO_FLATTEN_SCRIPT ${CMAKE_BINARY_DIR}/cmake/flatten.cmake) configure_file( "${CMAKE_SOURCE_DIR}/cmake/flatten.cmake.in" ${NEKO_FLATTEN_SCRIPT} IMMEDIATE @ONLY) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) # uninstall target configure_file( "${CMAKE_SOURCE_DIR}/cmake/uninstall.cmake.in" "${CMAKE_BINARY_DIR}/cmake/uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/cmake/uninstall.cmake) # package set(CPACK_OUTPUT_FILE_PREFIX ${OUTPUT_DIR}) set(CPACK_PACKAGE_VERSION_MAJOR ${NEKO_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${NEKO_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${NEKO_VERSION_PATCH}) set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") if (WIN32) set(CPACK_GENERATOR "ZIP") set(bin_archive_format zip) else() set(CPACK_GENERATOR "TGZ") set(bin_archive_format tar.gz) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") set(OS_NAME "win") elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") set(OS_NAME "osx") elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(OS_NAME "linux") elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") set(OS_NAME "freebsd") else() message( WARNING "unknown ${CMAKE_SYSTEM_NAME}" ) set(OS_NAME "") endif() if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64") set(arch_suffix "-arm64") else() set(arch_suffix ${arch_64}) endif() set(bin_archive_name_we "neko-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-${OS_NAME}${arch_suffix}") set(bin_archive_name "${bin_archive_name_we}.${bin_archive_format}") set(CPACK_PACKAGE_FILE_NAME ${bin_archive_name_we}) # set(CPACK_SOURCE_PACKAGE_FILE_NAME # "neko-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-src") include(CPack) ####################### # tests include(CTest) add_test(NAME -version COMMAND nekovm -version ) add_test(NAME test.n COMMAND nekovm test.n WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) add_test(NAME nekoc COMMAND nekoc WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) if (WITH_NEKOML) add_test(NAME nekoml COMMAND nekoml WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) endif() add_test(NAME nekotools COMMAND nekotools WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) ####################### # debian source packages if(UNIX AND NOT APPLE) add_custom_target(upload_to_ppa COMMAND ${CMAKE_COMMAND} -Dsource_dir=${CMAKE_SOURCE_DIR} -Dbin_dir=${CMAKE_BINARY_DIR} -Dsource_archive=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_archive_fat_name} -DNEKO_VERSION=${NEKO_VERSION} -P ${CMAKE_SOURCE_DIR}/cmake/upload_to_ppa.cmake DEPENDS source_archive_fat ) endif() ####################### # chocolatey if(WIN32) add_custom_target(package_choco COMMAND ${CMAKE_COMMAND} -Dsource_dir=${CMAKE_SOURCE_DIR} -Dbin_dir=${CMAKE_BINARY_DIR} -Dbin_archive=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${bin_archive_name} -Dbin_archive_name_we=${bin_archive_name_we} -DNEKO_VERSION=${NEKO_VERSION} -P ${CMAKE_SOURCE_DIR}/cmake/package_choco.cmake DEPENDS package ) endif() add_subdirectory(libs) neko-2-4-0/Earthfile000066400000000000000000000207511464615675700143270ustar00rootroot00000000000000VERSION 0.6 FROM ubuntu:jammy ARG DEVCONTAINER_IMAGE_NAME_DEFAULT=haxe/neko_devcontainer ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=$USER_UID ARG LINK_TYPE_DEFAULT=static # static or dynamic ARG LINK_DYNAMIC_PACKAGES="libgc-dev libpcre2-dev zlib1g-dev apache2-dev libmysqlclient-dev libsqlite3-dev libmbedtls-dev" vscode-dev-containers-scripts: FROM curlimages/curl:7.80.0 WORKDIR /tmp RUN curl -fsSLO https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/common-debian.sh RUN curl -fsSLO https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/docker-debian.sh SAVE ARTIFACT --keep-ts *.sh AS LOCAL .devcontainer/library-scripts/ devcontainer-base: FROM mcr.microsoft.com/vscode/devcontainers/base:0-jammy ARG --required TARGETARCH # Avoid warnings by switching to noninteractive ENV DEBIAN_FRONTEND=noninteractive ARG INSTALL_ZSH="false" ARG UPGRADE_PACKAGES="true" ARG ENABLE_NONROOT_DOCKER="true" ARG USE_MOBY="true" COPY .devcontainer/library-scripts/*.sh /tmp/library-scripts/ RUN apt-get update \ && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \ # Use Docker script from script library to set things up && /bin/bash /tmp/library-scripts/docker-debian.sh "${ENABLE_NONROOT_DOCKER}" "/var/run/docker-host.sock" "/var/run/docker.sock" "${USERNAME}" "${USE_MOBY}" \ # Clean up && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts/ # Configure apt and install packages RUN apt-get update \ && apt-get install -y --no-install-recommends apt-utils dialog 2>&1 \ && apt-get install -y \ iproute2 \ procps \ sudo \ bash-completion \ build-essential \ curl \ wget \ software-properties-common \ direnv \ tzdata \ python3-pip \ # Neko deps cmake \ ninja-build \ pkg-config \ libgtk-3-dev \ $LINK_DYNAMIC_PACKAGES \ # # Clean up && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* # Switch back to dialog for any ad-hoc use of apt-get ENV DEBIAN_FRONTEND= # Setting the ENTRYPOINT to docker-init.sh will configure non-root access # to the Docker socket. The script will also execute CMD as needed. ENTRYPOINT [ "/usr/local/share/docker-init.sh" ] CMD [ "sleep", "infinity" ] # VS Code workspace RUN mkdir -m 777 "/workspace" WORKDIR /workspace # Usage: # COPY +earthly/earthly /usr/local/bin/ # RUN earthly bootstrap --no-buildkit --with-autocomplete earthly: FROM +devcontainer-base ARG --required TARGETARCH RUN curl -fsSL https://github.com/earthly/earthly/releases/download/v0.6.30/earthly-linux-${TARGETARCH} -o /usr/local/bin/earthly \ && chmod +x /usr/local/bin/earthly SAVE ARTIFACT /usr/local/bin/earthly devcontainer: FROM +devcontainer-base # Install earthly COPY +earthly/earthly /usr/local/bin/ RUN earthly bootstrap --no-buildkit --with-autocomplete USER $USERNAME # Config direnv COPY --chown=$USER_UID:$USER_GID .devcontainer/direnv.toml /home/$USERNAME/.config/direnv/config.toml # Config bash RUN echo 'eval "$(direnv hook bash)"' >> ~/.bashrc USER root ARG DEVCONTAINER_IMAGE_NAME="$DEVCONTAINER_IMAGE_NAME_DEFAULT" ARG DEVCONTAINER_IMAGE_TAG=latest SAVE IMAGE --push "$DEVCONTAINER_IMAGE_NAME:$DEVCONTAINER_IMAGE_TAG" "$DEVCONTAINER_IMAGE_NAME:latest" devcontainer-rebuild: RUN --no-cache date +%Y%m%d%H%M%S | tee buildtime ARG DEVCONTAINER_IMAGE_NAME="$DEVCONTAINER_IMAGE_NAME_DEFAULT" BUILD \ --platform=linux/amd64 \ --platform=linux/arm64 \ +devcontainer \ --DEVCONTAINER_IMAGE_NAME="$DEVCONTAINER_IMAGE_NAME" \ --DEVCONTAINER_IMAGE_TAG="$(cat buildtime)" BUILD +devcontainer-update-refs \ --DEVCONTAINER_IMAGE_NAME="$DEVCONTAINER_IMAGE_NAME" \ --DEVCONTAINER_IMAGE_TAG="$(cat buildtime)" devcontainer-update-refs: ARG --required DEVCONTAINER_IMAGE_NAME ARG --required DEVCONTAINER_IMAGE_TAG BUILD +devcontainer-update-ref \ --DEVCONTAINER_IMAGE_NAME="$DEVCONTAINER_IMAGE_NAME" \ --DEVCONTAINER_IMAGE_TAG="$DEVCONTAINER_IMAGE_TAG" \ --FILE='.devcontainer/docker-compose.yml' devcontainer-update-ref: ARG --required DEVCONTAINER_IMAGE_NAME ARG --required DEVCONTAINER_IMAGE_TAG ARG --required FILE COPY "$FILE" file.src RUN sed -e "s#$DEVCONTAINER_IMAGE_NAME:[a-z0-9]*#$DEVCONTAINER_IMAGE_NAME:$DEVCONTAINER_IMAGE_TAG#g" file.src > file.out SAVE ARTIFACT --keep-ts file.out $FILE AS LOCAL $FILE build-env: # We specifically use an old distro to build against an old glibc. # https://repology.org/project/glibc/versions FROM ubuntu:bionic RUN apt-get update \ && apt-get install -qqy --no-install-recommends \ software-properties-common \ curl \ build-essential \ autoconf \ automake \ libtool \ file \ git \ ninja-build \ pkg-config \ libgtk-3-dev \ # # Clean up && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* # install a recent CMake ARG --required TARGETARCH ARG CMAKE_VERSION=3.25.0 RUN case "$TARGETARCH" in \ amd64) curl -fsSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh" -o cmake-install.sh;; \ arm64) curl -fsSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-aarch64.sh" -o cmake-install.sh;; \ *) exit 1;; \ esac \ && sh cmake-install.sh --skip-license --prefix=/usr/local \ && rm cmake-install.sh \ && cmake --version | grep -q "$CMAKE_VERSION" ARG LINK_TYPE="$LINK_TYPE_DEFAULT" # static or dynamic RUN if [ "$LINK_TYPE" = "dynamic" ]; then \ apt-get update && apt-get install -qqy --no-install-recommends $LINK_DYNAMIC_PACKAGES \ # # Clean up && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* \ ; fi build: ARG LINK_TYPE="$LINK_TYPE_DEFAULT" # static or dynamic FROM +build-env --LINK_TYPE="$LINK_TYPE" WORKDIR /src COPY --dir boot cmake extra libs src vm . COPY CMakeLists.txt CHANGES LICENSE README.md . WORKDIR /src/build RUN case "$LINK_TYPE" in \ static) cmake .. -DSTATIC_DEPS=all -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo;; \ dynamic) cmake .. -DSTATIC_DEPS=none -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo;; \ *) exit 1;; \ esac RUN if [ "$LINK_TYPE" = "static" ]; then ninja download_deps; fi RUN ninja RUN ninja test SAVE ARTIFACT bin/* package: ARG LINK_TYPE="$LINK_TYPE_DEFAULT" # static or dynamic FROM +build --LINK_TYPE="$LINK_TYPE" RUN ninja package # ARG --required TARGETOS # ARG --required TARGETARCH # RUN mv bin/neko-*.tar.gz "bin/neko-$(cmake -L -N -B . | awk -F '=' '/NEKO_VERSION/ {print $2}')-${TARGETOS}-${TARGETARCH}.tar.gz" ARG --required TARGETPLATFORM SAVE ARTIFACT --keep-ts bin/neko-*.tar.gz AS LOCAL bin/$LINK_TYPE/$TARGETPLATFORM/ package-all-platforms: BUILD --platform=linux/amd64 --platform=linux/arm64 +package extract-package: ARG LINK_TYPE="$LINK_TYPE_DEFAULT" # static or dynamic FROM +package --LINK_TYPE="$LINK_TYPE" WORKDIR bin RUN mkdir /tmp/neko && tar xf neko-*.tar.gz --strip-components 1 -C /tmp/neko SAVE ARTIFACT /tmp/neko neko test-static-package: ARG IMAGE=ubuntu:bionic FROM $IMAGE WORKDIR /tmp/neko COPY +extract-package/neko . ARG PREFIX=/usr/local RUN mkdir -p $PREFIX/bin \ && mv neko nekotools nekoc nekoml $PREFIX/bin \ && mkdir -p $PREFIX/lib/neko \ && mv *.ndll nekoml.std $PREFIX/lib/neko \ && mv *.so* $PREFIX/lib \ && rm -rf * \ && ldconfig RUN neko -version RUN nekoc RUN nekoml RUN nekotools test-static-package-all-platforms: ARG IMAGE=ubuntu:bionic BUILD --platform=linux/amd64 --platform=linux/arm64 +test-static-package --IMAGE="$IMAGE" neko-2-4-0/LICENSE000066400000000000000000002155431464615675700135130ustar00rootroot00000000000000Neko Virtual Machine (neko) and Neko Tools (nekotools) ====================================================== Copyright (C)2005-2022 Haxe Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Boehm GC -------- Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. Copyright 1996-1999 by Silicon Graphics. All rights reserved. Copyright 1999 by Hewlett-Packard Company. All rights reserved. Copyright (C) 2007 Free Software Foundation, Inc THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. Permission is hereby granted to use or copy this program for any purpose, provided the above notices are retained on all copies. Permission to modify the code and to distribute modified code is granted, provided the above notices are retained, and a notice that the code was modified is included with the above copyright notice. Neko Runtime Libraries ====================== std, sqlite, mysql5, ui ----------------------- Copyright (C)2005-2022 Haxe Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. regexp ------ PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Copyright (c) 1997-2007 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. zlib ---- Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). mysql ----- mysql.ndll uses the LGPL-licensed MariaDB Connector/C. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! mod_neko and mod_tora --------------------- Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Neko Compiler (nekoc) and NekoML Compiler (nekoml) ================================================== GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. {signature of Ty Coon}, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. mbedTLS ------ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. neko-2-4-0/README.md000066400000000000000000000155471464615675700137670ustar00rootroot00000000000000![NekoVM](https://cloud.githubusercontent.com/assets/576184/14234981/10528a0e-f9f1-11e5-8922-894569b2feea.png) [![Build Status](https://dev.azure.com/HaxeFoundation/GitHubPublic/_apis/build/status/HaxeFoundation.neko?branchName=master)](https://dev.azure.com/HaxeFoundation/GitHubPublic/_build/latest?definitionId=2&branchName=master) # Deprecated as of 2021-09-09 **Neko is not actively maintained anymore.** We keep it compatible with existing Haxe standard library and Haxe language features. But don't expect any new features in Neko itself and don't expect implementation of any new Haxe standard library API. # Neko Virtual Machine See https://nekovm.org/ ## Snapshot Builds Compiled binaries can be found in the "artifacts" link in the summary section of each [Azure Pipelines build](https://dev.azure.com/HaxeFoundation/GitHubPublic/_build?definitionId=2&_a=summary&repositoryFilter=2&branchFilter=14&statusFilter=succeeded). For macOS, Neko snapshot of the latest master branch can be built using [homebrew](https://brew.sh/) in a single command: `brew install neko --HEAD`. It will install required dependencies, build, and install Neko to the system. The binaries can be found at `brew --prefix neko`. Use `brew reinstall neko --HEAD` to upgrade in the future. Ubuntu users can use the [Haxe Foundation snapshots PPA](https://launchpad.net/~haxe/+archive/ubuntu/snapshots) to install a Neko package built from the latest master branch. To do so, run the commands as follows: ``` sudo add-apt-repository ppa:haxe/snapshots -y sudo apt-get update sudo apt-get install neko -y ``` Users of other Linux/FreeBSD distributions should build Neko from source. See below for additional instructions. ## Build instruction Neko can be built using CMake (version 3.x is recommended) and one of the C compilers listed as follows: * Windows: Visual Studio 2010 / 2013 / 2015 / 2017 * Mac: XCode (with its "Command line tools") * Linux: gcc (can be obtained by installing the "build-essential" Debian/Ubuntu package) Neko needs to link with various third-party libraries, which are summarized as follows: | library / tool | OS | Debian/Ubuntu package | |-----------------------------------------|-------------|-----------------------------------------------------------| | Boehm GC | all | libgc-dev | | OpenSSL | all | libssl-dev | | pcre2 | all | libpcre2-dev | | zlib | all | zlib1g-dev | | Apache 2.2 / 2.4, with apr and apr-util | all | apache2-dev | | MariaDB / MySQL (Connector/C) | all | libmariadb-client-lgpl-dev-compat (or libmysqlclient-dev) | | SQLite | all | libsqlite3-dev | | mbed TLS | all | libmbedtls-dev | | GTK+3 | Linux | libgtk-3-dev | On Windows, CMake will automatically download and build the libraries in the build folder during the build process. However, you need to install [Perl](https://www.activestate.com/activeperl) manually because OpenSSL needs it for configuration. On Mac/Linux, you should install the libraries manually to your system before building Neko, or use the `STATIC_DEPS` CMake option, which will be explained in [CMake options](#cmake-options). ### Building on Mac/Linux ```shell # make a build directory, and change to it mkdir build cd build # run cmake cmake .. # let's build, the outputs can be located in the "bin" directory make # install it if you want # default installation prefix is /usr/local make install ``` ### Building on Windows Below is the instructions of building Neko in a Visual Studio command prompt. You may use the CMake GUI and Visual Studio to build it instead. ```shell # make a build directory, and change to it mkdir build cd build # run cmake specifying the visual studio version you need # Visual Studio 12 2013, Visual Studio 14 2015, Visual Studio 15 2017 # you can additionally specify platform via -A switch (x86, x64) cmake -G "Visual Studio 12 2013" .. # let's build, the outputs can be located in the "bin" directory msbuild ALL_BUILD.vcxproj /p:Configuration=Release # install it if you want # default installation location is C:\HaxeToolkit\neko msbuild INSTALL.vcxproj /p:Configuration=Release ``` ### CMake options A number of options can be used to customize the build. They can be specified in the CMake GUI, or passed to `cmake` in command line as follows: ```shell cmake "-Doption=value" .. ``` #### NDLLs Settings that allow to exclude libraries and their dependencies from the build; available on all platforms. By default all are `ON`: - `WITH_REGEXP` - Build Perl-compatible regex support - `WITH_UI` - Build GTK-3 UI support - `WITH_SSL` - Build SSL support - `WITH_MYSQL` - Build MySQL support - `WITH_SQLITE` - Build Sqlite support - `WITH_APACHE` - Build Apache modules #### `STATIC_DEPS` Default value: `all` for Windows, `none` otherwise It defines the dependencies that should be linked statically. Can be `all`, `none`, or a list of library names (e.g. `BoehmGC;Zlib;OpenSSL;MariaDBConnector;pcre2;SQLite3;APR;APRutil;Apache;MbedTLS`). CMake will automatically download and build the specified dependencies into the build folder. If a library is not present in this list, it should be installed manually, and it will be linked dynamically. All third-party libraries, except GTK+3 (Linux) and BoehmGC on Windows, can be linked statically. We do not support statically linking GTK+3 due to the difficulty of building it and its own dependencies. Additionally, we do not support statically linking the BoehmGC library on Windows systems. #### `RELOCATABLE` Available on Mac/Linux. Default value: `ON` Set RPATH to `$ORIGIN` (Linux) / `@executable_path` (Mac). It allows the resulting Neko VM executable to locate libraries (e.g. "libneko" and ndll files) in its local directory, such that the libraries need not be installed to "/usr/lib" or "/usr/local/lib". #### `NEKO_JIT_DISABLE` Default `OFF`. Disable Neko JIT. By default, Neko JIT will be enabled for platforms it supports. Setting this to `ON` disable JIT for all platforms. #### `NEKO_JIT_DEBUG` Default `OFF`. Debug Neko JIT. #### `RUN_LDCONFIG` Available on Linux. Default value: `ON` Whether to run `ldconfig` automatically after `make install`. It is for refreshing the shared library cache such that "libneko" can be located correctly by the Neko VM. neko-2-4-0/boot/000077500000000000000000000000001464615675700134375ustar00rootroot00000000000000neko-2-4-0/boot/nekoc.n000066400000000000000000005116661464615675700147340ustar00rootroot00000000000000NEKO J{@Neko_Main@IOIO@Neko_Printer neko/Printer@ListList@Neko_Binast neko/Binast@Neko_Compile neko/Compile@LexerLexer@StringString@Neko_Xmlneko/Xml@Neko_Console neko/Console@Neko_Lexer neko/Lexer@Neko_Linker neko/Linker@Neko_Docneko/Doc@SysSys@Neko_Parser neko/Parser@ArrayArray@StackStack@ArgsArgs@BufferBuffer@CoreCore@Neko_Bytecode neko/Bytecode@Sys_exit@Array_map@Neko_Printer_create@Stack_dump@IO_close_in@List_rev@Neko_Bytecode_write@Neko_Binast_parse@IO_read_string@Core_@compare@String_length@IO_close_out@Neko_Doc_to_html@Lexer_input@Neko_Compile_compile@IO_stderr@Neko_Binast_header@Core_printf@Lexer_create@Args_String@Args_Int@Core_@pcons@Neko_Printer_print@Core_Neko_error@IO_write_file@Neko_Doc_error_msg@Core_@empty@Neko_Bytecode_read@Sys_version@Neko_Xml_parse@IO_file_contents@Neko_Parser_parse@Core_None@Lexer_line@String_sub@Neko_Parser_error_msg@Args_Void@Neko_Lexer_error_msg@Neko_Console_run@Stack_exc@String_get@Buffer_create@IO_printf@Core_Some@Sys_without_dir@Neko_Bytecode_dump@Neko_Doc_parse@Lexer_null_pos@IO_read_file@Neko_Bytecode_GlobalVar@Lexer_source@Neko_Linker_link@Neko_Compile_error_msg@Core_string@Sys_without_extension@Neko_Doc_status@Args_parse   J Compiling %s .n Dumping %s .dump Printing %s 2.neko' Releasing %s eBuilding documentation for %s .htmlException : %s  %s(%d): %s g/Neko Compiler v.@ - (c)2005-2013 Haxe Foundation Usage : neko [options] files... : dump bytecode-d : make bytecode release-z $ : parse and print neko source-p : make documentation-doc : set output directory-o+: run the console-console5 : link bytecodes files-linkB: verbose mode-vJ: set the bytecode version-versionRneko/Main@IO@String@Buffer@Core@Core_@print_union@String_blit@Buffer_add_char@String_create@String_sub@Core_assert@Core_ord@Core_Exit@Buffer_add_sub@Core_Neko_error@Buffer_string@Core_sprintf@Core_throw@String_length@String_get@Buffer_add@Buffer_create@Core_magic@Core_chr@Core_min@Core_invalid_argOverflowOverflowEofEofClosedClosedBlockedBlocked@loadstd@ file_open file_contents file_close file_readfile_read_char file_writefile_write_char file_flush&/_rbr>GPwbww file_stdin file_stdout file_stderrIO.inputIO.readA IO.read_buf} core/IO.nmlF        * 8 A  IO.write_i8S  IO.outputv    IO.write_ui16:  IO.write_i16e  IO.write_ui24        IO@String@List@Buffer@Core@List_iter@Buffer_string@Core_sprintf@Buffer_add_char@Core_throw@Buffer_add@Buffer_create@Core_chr@Core_invalid_arg@Core_Not_found@Core_ord@string_splitstd@string_splitA  String.getk  String.set  String.blit  String.sub      \n\t\r\\\"\%.3d T:String.unescapemW@serialize std@serialize@unserializestd@unserializeString@List@Array@Core@Core_@pcons@Array_make@Core_@aset@Array_list@Core_@compare@Array_create@Core_throw@Core_@empty@Array_sort@Core_invalid_arg@Core_Not_found@make@rmake7DList.hdwList.tl List.iter2[X?"-r List.chop` List.loopList.nth3 a  !l!List@Array@Core@Core_@aset@Core_throw@Core_@aget@Core_Not_found[, ]!@make!@merge_sortstd@merge_sort!!"Y"c"r"""#.#o###$asub#$*$Array@CoreNoneSomex$NoneSome$ Neko_error$Neko_error$Invalid_argument$Invalid_argument$Assert_failure$Assert_failure%Error%Error"% Stream_error/%Stream_error Not_found=%Not_foundExitK%Exit@sprintf std@sprintf@string()Y%{ __string = ; }%L&@compare&' Array.get' Array.set(@empty[]4(@pcons :: 8(@consP(_(b(j(y(((((Core.int( Core.float((Core.chr((()))%)<)S)])o:0l)Core.stream_token)Core.stream_junk)n*nekoCore@Buffer@Core@buffer_newstd@buffer_new@buffer_addstd@buffer_add@buffer_add_substd@buffer_add_sub@buffer_add_charstd@buffer_add_char@buffer_stringstd@buffer_string@buffer_resetstd@buffer_resetr0x00Buffer@Neko_Printer@IO@List@String@Neko_Astneko/Ast@Core@IO_write_char@List_iter@IO_write_string@String_make@Core_@compare@Core_None@IO_close_out@IO_write@Neko_Ast_s_constant@Core_assert@IO_printf@Core_fst66+6F6%sp66{;6 }(  ).%s,var 7while do if elsetrycatch %s function( %s return;return break;break continue $new(null)neko/Printer.nml%s => ^7%s:switch  => 8 default => :8 =>>.?neko/Printer@Neko_Ast@List@Lexer@String@Core@Core_@pcons@Core_Some@Core_@print_union@List_iter@Core_None@String_escape@Core_@empty@Core_string@List_mapTrueFalseNullThisIntFloatBuiltinIdentv?TrueFalseNullThisInt@Float@String@Builtin(@Ident5@VarWhileDoIfElseFunctionReturnBreakContinueDefaultTryCatchSwitchB@VarWhileDoIfElseFunctionReturnBreakContinueDefaultTryCatchSwitch SemicolonDotCommaArrow BraceOpen BraceClose ParentOpen ParentClose BracketOpen BracketCloseConstKeywordBinopComment CommentLineAEofSemicolonDotCommaArrowBraceOpenBraceCloseParentOpenParentCloseBracketOpenBracketCloseConstAKeywordABinop BCommentBCommentLine&B NormalWhileDoWhile3BNormalWhileDoWhileEConstEBlock EParenthesisEFieldECallEArrayEVarsEWhileEIfETry EFunctionEBinopEReturnEBreak EContinueENextEObjectELabelESwitchgBEConst~CEBlockCEParenthesisCEFieldCECallCEArrayCEVarsCEWhileCEIfCETry DEFunction!DEBinop2DEReturnFDEBreakSDEContinueENext`DEObjectqDELabel~DESwitchDDDDDDDEE*E=E|EEEHHHHtruefalsenullthis"$CKvarwhiledoiffunctionreturnbreakdefaultcatchswitchK=>/**///Lneko/Ast@Lexer@IO@List@String@LexEngine LexEngine@Core@Core_Some@List_array@Core_@print_union@String_blit@IO_input@String_create@Core_None@LexEngine_make_tables@String_sub@Core_assert@Core_fst@Core_@aget@Core_max@Core_Exit@Core_Neko_error@LexEngine_determinize@IO_read_string@Core_snd@String_length@String_get@Core_throw@LexEngine_parse@Core_@empty@Core_min@Core_invalid_arg@List_map Invalid_ruleLInvalid_ruleLLLLM=MAMHMMMMNN*OOcore/Lexer.nmlOPPQQLexer.empty_tableQQLexer@LexEngine@List@HashtblHashtbl@Array@String@Core@List_array@Array_make@Core_@print_union@List_iter@Array_map@Hashtbl_find@Core_None@Core_assert@Array_sort@List_mem@List_filter@Core_fst@List_rev@Core_compare@Array_iteri@Core_@aset@Core_@compare@Array_iter@String_get@String_length@List_exists@Array_init@Core_Some@List_fold@Core_ignore@Hashtbl_add@Core_@aget@Core_ord@List_assoc@Core_@pcons@List_concat@Core_Neko_error@List_sort@Core_snd@Array_length@Core_throw@Core_@empty@List_map@List_append@Hashtbl_createEmptyMatchStarPlusNextChoiceQEmptyMatchTRStaraRPlusnRNext{RChoiceRRcore/LexEngine.nmlRSTtTTTTTTTU%V?V|VWW.WQWhWyWWX,XX8Y\InvalidRegexpI\V\r\\\\W]]]]^\_LexEngine@Hashtbl@Core@Core_throw@Core_Not_found````a"a1a@aTaHashtbl@Neko_Binast@IO@Lexer@Array@Neko_Ast@Core@IO_read_byte@Neko_Ast_ETry@Core_None@Array_create@Core_assert@Neko_Ast_Builtin@IO_read_ui16@Neko_Ast_True@Neko_Ast_EConst@Core_fst@IO_read_i32@Neko_Ast_String@Neko_Ast_ESwitch@Neko_Ast_EFunction@IO_read@Neko_Ast_EBlock@Neko_Ast_NormalWhile@Neko_Ast_DoWhile@Neko_Ast_EObject@IO_read_ui24@Neko_Ast_EVars@Neko_Ast_EReturn@Array_get@Neko_Ast_EBinop@Neko_Ast_ELabel@Neko_Ast_Ident@Neko_Ast_EContinue@Neko_Ast_ENext@Core_Some@Array_add@Neko_Ast_EArray@Neko_Ast_EWhile@Neko_Ast_EIf@Neko_Ast_False@Neko_Ast_Int@Neko_Ast_This@Neko_Ast_Float@Neko_Ast_EParenthesis@Neko_Ast_Null@Core_@pcons@Neko_Ast_EField@Array_length@Neko_Ast_ECall@Core_@empty@Neko_Ast_EBreak@Core_errorneko/Binast.nml+lNBA7llInvalid const tag : l+-*%<<>>>>>|&^==!=>>=<<==&&||++=--=+=-=/=*=%=<<=>>=>>>=|=&=^=Invalid op tag \mnnnn oInvalid expr tag : #oInvalid binast headerrneko/Binast@Neko_Compile@Hashtbl@List@Lexer@Array@MapMap@String@Neko_Ast@Neko_Bytecode@Core@Neko_Bytecode_op_param@Neko_Ast_Builtin@Neko_Bytecode_SetIndex@Neko_Bytecode_trap_stack_delta@Neko_Bytecode_GlobalFloat@List_rev@Neko_Bytecode_Mod@Neko_Bytecode_Bool@Neko_Bytecode_Apply@Neko_Bytecode_MakeEnv@String_length@Array_iter@Neko_Bytecode_SetGlobal@Neko_Bytecode_Lte@Neko_Bytecode_Neq@Neko_Bytecode_AccIndex0@Array_set@Neko_Bytecode_AccIndex1@Neko_Bytecode_Or@Hashtbl_length@Core_Neko_error@Neko_Ast_EField@Core_snd@Neko_Bytecode_Not@Neko_Bytecode_GlobalString@Neko_Bytecode_AccEnv@Neko_Bytecode_SetThis@Core_@empty@Neko_Bytecode_ObjCall@Neko_Bytecode_Jump@List_length@Array_make@Core_@print_union@Neko_Bytecode_AccInt@Core_assert@String_sub@Array_sort@Neko_Bytecode_New@Neko_Bytecode_Pop@Neko_Bytecode_JumpTable@Neko_Ast_Ident@Array_add@Neko_Bytecode_PhysCompare@Neko_Bytecode_AccGlobal@Neko_Bytecode_Div@Neko_Bytecode_Ret@Hashtbl_add@Neko_Bytecode_Xor@Core_Exit@Neko_Bytecode_GlobalVersion@Neko_Bytecode_TypeOf@Array_length@Neko_Bytecode_SetField@Neko_Bytecode_Lt@Array_append@Neko_Bytecode_JumpIf@Neko_Bytecode_AccThis@Map_iter@Hashtbl_create@Neko_Bytecode_JumpIfNot@List_iter@Neko_Bytecode_SetEnv@Map_find@Map_add@Neko_Bytecode_Hash@Array_map@Neko_Bytecode_EndTrap@Neko_Ast_EConst@Neko_Bytecode_AccStack@Hashtbl_exists@Neko_Bytecode_Shl@Core_@aset@Core_compare@Neko_Ast_EBlock@Neko_Bytecode_GlobalDebug@Core_@compare@Neko_Bytecode_SetArray@Neko_Ast_EVars@Neko_Ast_EBinop@Neko_Bytecode_Eq@Neko_Ast_EIf@Neko_Bytecode_AccTrue@Core_ignore@Neko_Bytecode_Gte@Neko_Ast_Int@Neko_Bytecode_Sub@Core_@pcons@Neko_Bytecode_TailCall@Core_sprintf@List_sort@Core_throw@Neko_Ast_ECall@Neko_Bytecode_Mult@Map_empty@Neko_Bytecode_AccField@Neko_Bytecode_AccIndex@Hashtbl_iter@Neko_Bytecode_Shr@Hashtbl_find@Core_None@Array_create@Neko_Bytecode_UShr@Neko_Bytecode_IsNotNull@Core_fst@Neko_Bytecode_AccFalse@Neko_Bytecode_AccNull@Neko_Bytecode_Push@Neko_Bytecode_SetStack@Neko_Ast_iter@Neko_Bytecode_MakeArray@Neko_Bytecode_AccArray@Neko_Bytecode_Compare@Neko_Bytecode_AccBuiltin@Neko_Bytecode_Call@Core_Some@Neko_Bytecode_IsNull@Neko_Ast_This@Lexer_null_pos@Neko_Bytecode_And@Neko_Bytecode_GlobalVar@Neko_Bytecode_AccStack0@Neko_Bytecode_Add@Neko_Bytecode_AccStack1@Neko_Bytecode_Loop@Neko_Bytecode_GlobalFunction@Core_string@Neko_Bytecode_Trap@List_map@Neko_Bytecode_GtXEnvXStackXGlobalXFieldXIndexXArrayXThis*uXEnvuXStackuXGlobaluXFielduXIndexuXArrayXThisuErroruuvvStack alignment failurewwxxxyByiyyyyyNzzzzzBreak outside a loopzContinue outside a loop{{array2{Q{{!|@|apply_|~|0Variable declaration must be done inside a block|neko/Compile.nml|#}2Label is not supported in this part of the programDuplicate label =}}tnulltinttfloattbooltstringtobjecttarray tfunction tabstract^Invalid access;+ӉUnknown operation5]istruenottypeofhashnewcomparepcomparegotoUnknown label qthrowߖInvalid $goto statementaconcatӗ.call$tmp!:ɟo:GLabel failure %d %d %s %s͠Р"d_3CS|neko/Compile@Map@Core@Core_compare@Core_@print_union@Core_@compare@Core_throw@Core_assert@Core_Not_found@Core_invalid_arg@Core_maxNodetEmptyNode² core/Map.nml*Map.remove_min_bindingDLUe!TƷ:BMap@Neko_Bytecode@Hashtbl@IO@List@Array@String@Buffer@Core@Array_make@IO_read_byte@Core_@print_union@List_iter@Array_create@Hashtbl_find@String_make@IO_write_i32@Core_assert@IO_read_ui16@IO_read_i32@Array_iteri@Core_@aset@IO_read@IO_write_ui16@IO_write_string@Array_iter@String_length@Buffer_create@Core_invalid_arg@Array_get@IO_printf@IO_read_char@Array_init@Array_add@IO_write_char@Buffer_add_char@Array_set@IO_write_byte@Hashtbl_add@IO_write@Core_@aget@Core_@pcons@Core_Neko_error@Buffer_string@Core_sprintf@String_escape@Array_length@Core_throw@Core_@empty@Core_string@Core_error@Hashtbl_createAccNullAccTrueAccFalseAccThisAccIntAccStack AccGlobalAccEnvAccFieldAccArrayAccIndex AccBuiltinSetStack SetGlobalSetEnvSetFieldSetArraySetIndexSetThisPushPopCallObjCallJumpJumpIf JumpIfNotTrapEndTrapRetMakeEnv MakeArrayBoolIsNull IsNotNullAddSubMultDivModShlShrUShrOrAndXorEqNeqGtGteLtLteNotTypeOfCompareHashNew JumpTableApply AccStack0 AccStack1 AccIndex0 AccIndex1 PhysCompareTailCallLoopAccNullAccTrueAccFalseAccThisAccIntAccStackAccGlobalɿAccEnvֿAccFieldAccArrayAccIndexAccBuiltinSetStack SetGlobalSetEnv$SetField1SetArraySetIndex>SetThisPushPopKCallXObjCalleJumprJumpIfJumpIfNotTrapEndTrapRetMakeEnvMakeArrayBoolIsNullIsNotNullAddSubMultDivModShlShrUShrOrAndXorEqNeqGtGteLtLteNotTypeOfCompareHashNewJumpTableApplyAccStack0AccStack1AccIndex0AccIndex1PhysCompareTailCallLoop GlobalVarGlobalFunction GlobalString GlobalFloat GlobalDebug GlobalVersionGlobalVarYGlobalFunctionfGlobalStringwGlobalFloatGlobalDebugGlobalVersion Invalid_fileInvalid_fileField hashing conflict  and Neko.Bytecode.write_debug_infosOdNEKO~{2Neko.Bytecode.read_debug_infos[Neko.Bytecode.loopdkneko/Bytecode.nmlnglobals : %d  nfields : %d codesize : %d ops , %d total  GLOBALS =  global %d : %s  function  nargs string "float debug %d ops %d bytesversion  FIELDS =  %s%s%.8X CODE = %.6X %6d %s  AccField  AccBuiltin  SetField END neko/Bytecode@Neko_Xml@List@Lexer@String@Neko_Ast@XmlXml@Core@List_iter@String_split@String_concat@Neko_Ast_Builtin@Neko_Ast_EConst@Neko_Ast_True@List_rev@Neko_Ast_String@Neko_Ast_ESwitch@Neko_Ast_EBlock@Core_@compare@Xml_attrib@String_unescape@String_length@Neko_Ast_EVars@Neko_Ast_EBinop@Neko_Ast_EWhile@Neko_Ast_EIf@Neko_Ast_Int@Neko_Ast_Float@Xml_Node@Neko_Ast_Null@Core_int@Core_@pcons@Core_Neko_error@Neko_Ast_EField@Neko_Ast_ECall@Core_throw@Core_@empty@Xml_node_text@List_append@Xml_node_name@Core_@print_union@Neko_Ast_ETry@Core_None@Core_assert@String_sub@Xml_firstNode@Neko_Ast_EFunction@Neko_Ast_NormalWhile@Neko_Ast_DoWhile@Neko_Ast_EObject@String_get@Neko_Ast_EReturn@Neko_Ast_s_constant@Core_invalid_arg@Neko_Ast_ELabel@Xml_nodes@Neko_Ast_Ident@Neko_Ast_EContinue@Neko_Ast_ENext@Core_Some@Neko_Ast_EArray@Xml_parse@Neko_Ast_False@Neko_Ast_This@Neko_Ast_EParenthesis@Lexer_null_pos@Neko_Ast_EBreak@Core_string@List_mapError neko/Xml.nml p:Neko.Xml.error'ifsvbgcaonextobjectlabelneko &4caseQUnknown node name : WNeko.Xml.parseO+Cneko/Xml@Xml@List@IO@String@Buffer@Core@Core_@print_union@List_iter@List_find@IO_write@String_concat@List_filter@List_hd@String_lowercase@Buffer_string@IO_write_string@Core_snd@String_escape@Buffer_add@Buffer_create@Core_invalid_arg@IO_printf@List_mapPCDataCDataDocumentlNodePCDataCDataDocumentError@parsero:0*@parse std@parse_xml>Tj Xml.firstNode Xml.nodes Xml.node_name Xml.node_text6 Xml.attrib<%s%s="/> 7Xml@Neko_Console@IO@Hashtbl@Neko_Compile@Lexer@Neko_Parser@Array@String@ReflectReflect@Stack@Neko_Lexer@Buffer@Neko_Bytecode@Core@Neko_Parser_parse@IO_input@Reflect_module_get_global@Hashtbl_find@Lexer_line@Stack_dump@Core_nprint@String_sub@Neko_Parser_error_msg@IO_close_in@Core_fst@Reflect_neko_value@Hashtbl_replace@Neko_Lexer_error_msg@Neko_Bytecode_write@Reflect_module_read@Array_iteri@IO_write_string@IO_read_all@IO_read_string@Reflect_module_set_global@Stack_exc@String_length@String_get@Buffer_add@Buffer_create@IO_stderr@Core_print@Lexer_input@Neko_Compile_compile@IO_printf@Buffer_add_char@Lexer_create@Lexer_source@IO_read_file@Reflect_module_execute@Buffer_string@Core_Neko_error@IO_stdout@Neko_Compile_error_msg@IO_read_line@Neko_Bytecode_read@IO_stdin@Hashtbl_create<h> #load @console  Exception : neko/Console@Neko_Parser@IO@Neko_Binast@Lexer@Neko_Xml@Neko_Ast@Neko_Lexer@Buffer@Core@Core_@print_union@Core_stream_junk@Neko_Ast_ETry@Core_None@Neko_Ast_Keyword@Neko_Ast_Default@Core_stream@Neko_Xml_parse_string@Lexer_punion@Neko_Ast_EConst@Core_fst@Neko_Ast_ESwitch@Neko_Ast_EFunction@Neko_Ast_NormalWhile@Neko_Ast_DoWhile@Neko_Ast_EBlock@Core_Stream_error@Core_stream_pos@Neko_Ast_EObject@IO_read_string@Neko_Ast_s_token@Neko_Ast_EVars@Neko_Ast_EReturn@Buffer_create@Lexer_input@Neko_Ast_EBinop@Core_stream_token@Neko_Ast_ELabel@Neko_Ast_pos@Neko_Ast_EContinue@Neko_Ast_Ident@Core_Some@Neko_Ast_EWhile@Neko_Ast_EIf@Neko_Ast_EArray@Lexer_create@Neko_Ast_Int@Neko_Ast_EParenthesis@Lexer_null_pos@Neko_Binast_parse_from_string@Core_@pcons@Neko_Ast_Eof@Neko_Ast_EField@Core_Neko_error@Core_snd@Core_throw@Neko_Ast_ECall@Neko_Lexer_expr@Core_string@Core_@empty@Neko_Ast_EBreak@Lexer_token UnexpectedUnclosed Invalid_nxmlUnexpectedUnclosedInvalid_nxmlError Unexpected  Unclosed Invalid nxml (Xgh p  : Z B-!""#%&g'')"** +neko/Parser@Neko_Lexer@Hashtbl@List@Lexer@String@Neko_Ast@Buffer@Core@List_iter@Neko_Ast_Default@Neko_Ast_Keyword@Neko_Ast_Semicolon@Neko_Ast_BraceOpen@Neko_Ast_Function@Neko_Ast_ParentOpen@Neko_Ast_BracketClose@Neko_Ast_Builtin@Lexer_punion@Neko_Ast_True@Lexer_char@Neko_Ast_String@Neko_Ast_Return@Neko_Ast_Continue@Neko_Ast_While@Lexer_current@String_length@Neko_Ast_BracketOpen@Core_chr@Neko_Ast_Try@Neko_Ast_Int@Neko_Ast_Float@Neko_Ast_BraceClose@Neko_Ast_ParentClose@Neko_Ast_Null@Buffer_reset@Core_@pcons@Core_int@Core_sprintf@Core_Neko_error@Neko_Ast_Switch@Lexer_build@Core_throw@Neko_Ast_Do@Core_@empty@Neko_Ast_Binop@Neko_Ast_s_keyword@Core_@print_union@Hashtbl_find@Neko_Ast_Dot@Neko_Ast_Arrow@String_sub@Neko_Ast_If@Neko_Ast_Else@Neko_Ast_Catch@String_get@Buffer_add@Neko_Ast_Ident@Buffer_add_char@Neko_Ast_Break@Neko_Ast_Const@Neko_Ast_False@Neko_Ast_This@Hashtbl_add@Lexer_empty@Neko_Ast_Comma@Core_ord@Neko_Ast_Comment@Core_Exit@Lexer_data@Neko_Ast_Eof@Buffer_string@Neko_Ast_Var@Neko_Ast_CommentLine@Lexer_token@Lexer_curpos@Hashtbl_createInvalid_characterUnterminated_stringUnclosed_comment Unclosed_nxmlInvalid_escaped_characterInvalid_escape^+Invalid_character+Unterminated_stringUnclosed_commentUnclosed_nxmlInvalid_escaped_character+Invalid_escape+Error+Invalid character '%c'Invalid character 0x%.2XUnterminated stringUnclosed comment Unclosed nxmlInvalid escaped character %dInvalid escape sequence+v,,,,,,[a-zA-Z_@][a-zA-Z0-9_@]*[-!=\*/<>&|^%\+:]9-E-Q-]-Continueh-u--------\[---[ ]+0x[0-9a-fA-F]+[0-9]+ [0-9]+.[0-9]*.[0-9]+-#.3.C.S.c.k../\*b/ //[^ ]* ?/?////60\*/90\*S0[^*]+m0v0\\"0\\\\0\\n0\\t0\\r0\\[0-9][0-9][0-9]+16191[^\\"]+S1\1i11[^<]+1neko/Lexer@Reflect@List@Core@Core_@print_union@Core_magic@Core_invalid_arg@List_mapVNullVIntVFloatVBoolVStringVObject VAbstract VFunctionVArray<VNullVIntN=VFloat[=VBoolh=VStringu=VObject=VAbstract=VFunction=VArray=@module_readstd@module_read@module_namestd@module_name@module_exportsstd@module_exports@module_loaderstd@module_loader@module_execstd@module_exec@module_nglobalsstd@module_nglobals@module_global_getstd@module_global_get@module_global_setstd@module_global_set@module_code_sizestd@module_code_size@module_read_pathstd@module_read_path Reflect.value==[>>>???-?f?n?{??????Reflect@Stack@IO@Array@Core@Core_@print_union@IO_stdout@Array_iter@IO_write@IO_printf CFunctionModulePosACFunctionModule-BPos:B@make_stackKBBBCalled from a C function $Called from %s (no debug available) Called from %s line %d BC,CStack@Neko_Linker@Hashtbl@IO@List@Array@Neko_Bytecode@Core@Array_make@Neko_Bytecode_JumpIfNot@Array_list@List_iter@Neko_Bytecode_op_param@Core_None@Array_map@Hashtbl_find@Array_create@Core_assert@IO_close_in@Hashtbl_replace@Core_Error@Neko_Bytecode_write@Neko_Bytecode_AccNull@Neko_Bytecode_GlobalDebug@Core_@compare@Neko_Bytecode_Push@IO_close_out@Core_print@Array_index@Array_get@Neko_Bytecode_SetGlobal@Core_Some@Neko_Bytecode_AccBuiltin@Neko_Bytecode_Call@Array_add@Neko_Bytecode_AccGlobal@Array_set@Core_ignore@Hashtbl_add@Core_@aget@IO_read_file@Neko_Bytecode_GlobalVar@Core_@pcons@Neko_Bytecode_GlobalVersion@Core_Neko_error@Core_snd@Core_throw@Array_length@Array_append@Core_@empty@Neko_Bytecode_JumpIf@Neko_Bytecode_GlobalFunction@IO_write_file@Neko_Bytecode_read@Neko_Bytecode_Jump@Neko_Bytecode_Trap@Hashtbl_createFile not found : (EEE&Linking modules with different version6GQGiGGneko/Linker.nmlloader loadmoduleRecursive loading Cannot link not constant fileexportsGK,LYL OOneko/Linker@Neko_Doc@IO@List@Lexer@String@Xml@Buffer@Core@Core_@print_union@Core_stream_junk@List_iter@Core_None@Core_stream@String_split@Lexer_punion@String_concat@Lexer_char@Core_fst@Core_Stream_error@Core_stream_pos@Lexer_current@Buffer_add@Buffer_create@Core_stream_token@IO_printf@Core_Some@Xml_parse@Buffer_add_char@Xml_to_string@Lexer_empty@IO_write@Lexer_null_pos@String_escape_char@Buffer_reset@Core_Exit@Core_@pcons@Lexer_data@Core_int@Core_Neko_error@Buffer_string@Lexer_build@Core_snd@Core_throw@Core_string@Core_@empty@Lexer_token@Lexer_curposTBase TAbstractTCustomTFunTOptTStarTMultTObjTNamedTPoly TFunction5QTBaseQTAbstractQTCustomQTFunRTOptRTStar!RTMult.RTObj;RTNamedHRTPolyYRTFunctionjRwRFunctionRDocumentRBeginEndQuestion DoubleDotPOpenPCloseBrOpenBrCloseFieldQuoteSharpDocREofBeginEndStarQuestionArrowDoubleDotPOpenPCloseBrOpenBrCloseCommaFieldOrQuoteSharpIntSDocSIdentS Invalid_char Unknown_type Unclosed_doc Invalid_doc TUnexpected]TInvalid_charjTUnknown_typewTUnclosed_docInvalid_docTErrorTT/****/->'#TInvalid character  Unknown type Unclosed doc tag Documentation is not XHTML validOUUUU/\*\*UU[^/]+UU\*\*/VVV%V0V;VFVQV\VgVrV\?}VVV>W[ ]+JWcW$?[a-zA-Z_@][a-zA-Z0-9_@]*wWWWWWX YUZZintfloatstringboolanyvoidnumber [list_pa.b1cWc'%s#%s ->  | c?d%s :  %s function:%ddf  f f' %s :  . %s(-g  gineko/Doc@Sys@List@Array@String@Core@Array_make@Core_Some@String_split@Core_None@String_concat@Core_assert@List_rev@sys_exit std@sys_exit@get_env std@get_env@get_cwd std@get_cwd@exe_pathstd@sys_exe_path@sys_file_typestd@sys_file_type@sys_read_dirstd@sys_read_dirpp\ core/Sys.nmlCqqqstd@sys_existsq std@put_envq std@set_cwdqdirqqSys@Args@List@Sys@Hashtbl@Array@Core@Sys_exit@Core_int@Core_@print_union@List_iter@Core_Neko_error@Hashtbl_find@Array_length@Core_throw@Core_print@Hashtbl_add@Sys_args@Core_@aget@Core_printf@Hashtbl_createVoidsVoidsStringsIntsInvalidsInvalid Options :  %s %s st"t-help--help.tDtInvalid argument : MtvArgs6neko/Mainneko/Main.nmlIOcore/IO.nmlStringcore/String.nmlListcore/List.nmlArraycore/Array.nmlCorecore/Core.nmlBuffercore/Buffer.nmlneko/Printerneko/Printer.nmlneko/Astneko/Ast.nmlLexercore/Lexer.nmlLexEnginecore/LexEngine.nmlHashtblcore/Hashtbl.nmlneko/Binastneko/Binast.nmlneko/Compileneko/Compile.nmlMapcore/Map.nmlneko/Bytecodeneko/Bytecode.nmlneko/Xmlneko/Xml.nmlXmlcore/Xml.nmlneko/Consoleneko/Console.nmlneko/Parserneko/Parser.nmlneko/Lexerneko/Lexer.nmlReflectcore/Reflect.nmlStackcore/Stack.nmlneko/Linkerneko/Linker.nmlneko/Docneko/Doc.nmlSyscore/Sys.nmlArgscore/Args.nml{"@f>f>jR>Z>b>Z>b>Z>$Z>>v>>J>4^r>N2tv^j>Nf*$vf^Z>Nf*$v^>Jz*L>vfv^4 <>Jz*$vfr>N^^>Nf*$zz>>F&$Z>>vT^bbb>Njvb>fv>"" 0>bnn.D>>>>n&>>>Jj*6<>F*t~~>Vr~rr`RD.< x>b& Zj.*D>F*D~&$&T6F p<$2$^>>j>$v^^VVb>Jzr,v^^VVJf>J>rr>>"T~:>ZNJN>*x>"4x>\ZJNbz:>"D>lZ^bb>:$Z^2$Z^b>$Z^fn,bf&8ZZZ"T  D"$> $>V:$^>>j>$Rfb>Jzr>,^rr:$>Vb:$>V^*$>Vbr:$",bff& ZZZZ"$> " ZN^nDbv$>&$>>&$vv$vv<>Z&>$fj>ph>TV*$6$>>z   $,&4>6<*tn>>>b***$&,&&4>>6<*L2ph>>&$Z^VVb>r>Fr>>>>>>>>>>>>>>>>D6hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hT6h:h46h$6hh@>@@f>,ZvV>Jbr>>r>$ZvV>Jbr>>r>L$* "PNZ>$NN>6,>ZH> H<>Z&<>Z&<>Z>><>Z(>&(<>npp`"<>^&j>&`>Z&z>>.L>Z0>:0<>Zh>>h<>Z&>>&<>Z&>><>Z&>:<>ZH>:H<*$>Z>><>Z>>D>Z>.N><2$ZN>^&x>x<>Z>:D>Z>:L>Z&8>>8(N:Z>>>d>Zv>>Z">>Df.Zf*"JNVN^>V^^b4><$6,JVn>F>Rb:,&$:,ZN^b:4ZN^v:4NNV^j6F:4>4NVV^^>4NNV^z6N>.4>4NVN^>^N&"X >>>>*<>n>r>>F>rzz:ܞN>V^NbN^>J^^b*$ZNNrZ^^^>>Fb$Z^RNb^>J^^b"$nRfbbZZNR>FbN^>z^NfJR>FbbN^Z~>>N^N>Fj$~jR>>D $$:$2$NN$Zv$Zv$ $>V$$$2$$2$>$>$&$:$RZZ.,^RNjr>V^zZ.,fv^>fZ^f6<X > >>,>J>N>N>N>N>N>x44bR^^$ $$$$$ $44 $, $ $$ $ $ $ $ $&$ D>>>>>>>>>>>>>6 ~$$ t $ $dd d dt 4 ldD 4>>>>>>>> >>>>:44Z>< < <>>>.<><>>L><><><><:<6<$>>*<><>\>L:D6D d> <>>>>>>>>>>>*  "($&$$z~~~~>$ $ $ ,JN>>> >>>>:dJ4 $ $ 4 4< $&d66x*l*tz$>>>>>>>>> >>>>>>>>>4<>J>N>N><,jnnnnnn:D&, >L&, rvbb"< D $ $ 4 l tD4<T \ L $ $, 4 \<4D< $ D>>>>>>>>>>>>>>>>>>>>>>"8J N"$V>^&$>Z>$V~>*\>dVZj&Vz>^j2>Z2>~Z>2 VZjfZ6@Vn>>>>"P$ff> 4fZ:PV>PVj>F>PXVjZ>Pf>"D Lf.P>>fZ>P"frjZ> P28f^>n>^Pj>P&P2fv~>Pf>nj>P2R>zv~v>>&P>.P f:P>8.PHf2PpVj^2P&&P&f&>>|Rf>PP> P2`f^j^> 4 <>ZjfZ>Df>P Z>>F> (&,~jfrf*(&db>Jz&(nZj&>> 4&T.|r^f^*>!"# >>>>>>>>>>>>>"d>>>>>>>>>>>>:>>>>>>>>>>>>>>>>>>>>>>><>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>2,>$>$>.$> $> $> $> $>\V>^> &l>R>*X`>>>`N>`>`>`$>2`,>"`<,2`2l>>`2t>>>>>`2|>>`>`2>>`>2>L`>2>D.`>"`>4`2>>>>>.`>>>Lr>PV>>> XN6X*X2X$>X,> X4> X<> X2D>>>>XL> XT&X\> X>2d&LX>2l&D&Xt> X|> 4 X2>>>>2X>nRRR(,&(4(<> (D"(L(t>>RRRRRRRRRRRRR>>jRRRRRRRRRR(d&(l&(t(|2("(%"'>"$N,J~>D $J^NhNbNN@8$$Z^ff^^^"fzzVf>fVZ>nvvrr$Z^rV:,Fbvvf2>"<:>Fn^^^^z>RR^^V>N>vf>^vfvVb>VX^bb"P,>> d>Fj:>>,br>\>lZ>$>>>>^>JX>)"+h @>>>>>>>>>>>:$~ >>>>$^>>Z>>^.>*T&>Z>> h>>>hXN>4>v2>.<>&$>DJ &<>n>&$:,>>$^>^D^j>vl^j>v>"^>>>F&>*R>>"D>>>>>>>*>>R>b> @>>^>X>>4>"$>.>D~ &>"L>^f>.dF:(>"$Nrv>R(:d\>fJ:>"$Nrvz^r>F>>J>>2$>>>>0R8:Z^^VVVV:>> R>Jr>J<>V*DjRb>Z>j*4 <,jRn>JZ>f:DzH.4>^>$>>>J> $2$fn>>>:<( <( <( <( <( <( <( D( l>J:  <>J:  <>J: & fN^>f>^>. ~r>  D>V>f>.X ^>>X p RVbbrr>>~6 6 6 , >>n^r>v>"  >>  >*  -"/$"$$vn$:$:$:$>$*> $ $ $ 4 $ $ $ $>>>>-+>>>>>>>>>>>>>>>>>4>>>>>>\N>$> |l&$&4 D><:$>. d>,*, $|>6<:<6< <>>>>>>>>>>>>>>)'>>>>>>>>>>:<4>NL $ $ d L $ L"d,*8 T>>>>>>>:%#>>>>>>>>>>>>>d>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>,>>>>>>>>>>>>>>>>>>>$ $ $ $ $ $ $ $ t >>>>>>>&!>>>>>4 D ,, D>\$>>* >2|>>>>6"1"3 .,^^bnj>&$Zb>> ,>>R   $,.4.<*D*L*T*\.>>>>>V H H H$H,H4H<HDHLHTH\HdHlHtH|HHHHHHHHHHHHHHHHHHHPHX.HZ>4^b*^.$ ,b> $r:^b*8^^>Vf>Jx>>>>2x$>&x4>&xD2xTb>xlb>:xb> x6,>&xbf6xbf6xbf>xbb>x>R> x^ff>xx> x(x8> xHxXbf*xx>"x>"x.x>x>x>J> xb>:x.x&8Z 0$>^*>>>>>>>>>>>>>>>>>>>: 4\> L x>>,>\6<>>" >>>>>215"7 >>>>>>>>>>>*`>>$ $>>>>>>>>>>>>Nh""2.N""".>>>>.">">$&$f>>V>J>8>nz>J86D^> ,>Fv>Z2<>>Zz>L>>" Zz>L>>`Zz>4>"$JJ>>"$bzr>dNRVVf^^>$.xVn*$.>FnZZ*$~>~>$>>,V>z^z8>$8(R< 0LZz>>j>F> r>,~>T~>H ~>0 J>^8 >8 $>J>n( $( $( 4(  V>TVv>Fv>>>> > R>Z* >,>>>>: b>F $> $~>>6 Z>>>>@ @ $~>>>@ @ $~>& >t>R:40  >>> r>^>Fv>>>jv>F>^V^v>Fv:> N:TZ>F>Fv>F>". > >>>>"8 >F>"x r > R>>v>>fVN 4>>>> ^>>>>> ~>>>~v>, 4>Z > $> >nrrr2 ,> 4>> <>> L>>>>r> > > $: ,: 4: <: D: L: &\>  FvfJ> >Vv>V 6D>b>>: >"~> > H>>>JvfJ>p>Vv>Vp6D2X>"|~"X2H`H> rjH>>>rjH>"rjvj H&p:H>n>>*>$> ,>2.d>n>>*>$> ,>j~2(.|>>>>>>>>b*p*p*p$&p,&p4&p<&pD&pL&pT&p\&pd&pl&pt&p|&p&p&p&p>>>>>>>>nv.(4rjz(\rjz(fVnf>Zjv>>Nf~> (fVnf>Zjv>>Nf~> (`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(>>>>v*>>Dr*>>.,r*>>\r*>>>>>*t>> >>&rjv6(r>&t>^2xdZ4J JlRh$Nr>^>Fj>>r>jv>F> $z>>>>>F>.><>zZ>~f.TRj>N:\>^>(b>>>>(tf>,r2>>>>>>v*>>:,r*>>:Dr*>>:\r*>>:tr*>>>>rjv*>>>>6rjv*>>>>>">>>>j:4>f"$6< D>>R>f^>j>2x>x$>D>&>*>>T>>~>>Vfv$$r>>> $r^>"> @@$>JR>>>>Z2>:>>>$v:>> (r2Dr2V>^2>$f>.>z2dbr>~2Tbr>~2Vjvz:4\V>vZ>&t V>ZzT$Vjjvj> >VVj>>>>&$&&4R>&dV>~v6> r>>>>>>>>>>>>>>>>f>N>>>>>>^>V>J>>z>>>>v~&6L>>>>&>>>>>>2>>>  $rj~>>h h$rR>>>*>>r>>>"rjv>6<>>@^>RvjbvZjZ:"^>RbvZv>f>2Rvjzv>"$^Zzv6.2`^>^vj>>>jbZjV>^z~Z> >2>>>>>>vV$>^20>Z0>0$R>N64`<>>R^>>V>N64<>>R^>>0r>&Hf*&`>NJ>h>>r~h6>>>>Z>>>>>fnZ>>>>(>>>>> 8Fzvzj`>N >b2>$^> L TV>8>"rj> 4 8 <>^2p >>.p $^>$ ,Vf> 8>8"~>vf6$~>4zj>"Vjj~:$"!!^^!"T 4!$Z`!JNN< !J$ZbP!>bvnZ>~RR^^Z^~:$&8",>Z^~f.L"Tf>b:>9";>>>>> \>jV8<>>2$^^j>>>>>R>>>>>f:.,6Tj>>>>>R>>>>>f:.,6T>>$>>jpn.pD>>V>>>J.D~ $r>"d HD 4>4>>n>v^f>f>>>">2`D>>n>J>F. >\*4>>n>R>>>>V  >2\hD>>n>>^ >D*,>Z>bj>N> L&,>Zp>ff>>&p`N> \"<>Z>>>&N> <6>>>*>>,, <$">D>D.T < < $ * l l >>>>>>>*972="?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> L*4$>>>>>>>>>.h: h>  h h> h">>>>:h:h  >>>$ZJn>>>>":.$ZJn>>>>":.$ZJn>>>>":L|v>2Z^:d lR>V`$>J:>4bf>Nr^>V>n^>$VbZZV>J>zb^z>zz6$N^r^V>N>^~~VV>"|> *T>f>>>>2>>$>> ,>64>TbV>>>>>>>>>RRRR@ ,.@ 4>@ <.@ D.@ L>F@ \>@ d>@ l.@ t.@ |.@ >F@ nR@ .@ .@ .@ >>@ >>@ >>@ >>Z@ .@ .@ nRRRRRRRRRRRRRRRRRRRRRRRR@  .@  nRRRR@ @ >^*@ X J^>F>Z>vz>N>N6Vj>N~~f:L T $> 0 & ^^f.  >L"|"4ZbnVb>Z>RZrnZjVb>jr>Fn>F>Vbb>r>V>H R^Vb>z>NbVV>@ >> V*d>2X$^>X<^>XT.Xdv*X|.XXZb^Jn>0>",r20>HfzZ>>>>>b>p>p>p.$>p,&p*L>J>:Fnzbbb>>>Z>>.V~VV^fb>Z.>>>$>,&"<>>>>>>>>>N   $,4&<DL>>>>T\&d>>>>lt|>>>>>>>>>>>> (08@HPX`hpx> >N>j>F>R","DhL&, 4.>>> XV>n:`>n>:`,> `46`Drr>>`d6` X*>>>>>^>>>>>>>>>V& & & .$" ., .4" .< .D" .L" .T" .\ .d" .l" .t" .|" .6 .6 .6 .6 ." ." ." ." ."  2>.V>N>N>N>brf> PnrNV> > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>L>N$ " : t> >>>>>>=7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>TDT>$ $ $ $ <&D*D*D*$ l \ , <, $ >" *@>>>2>d>|>>>X>2>>>>>>" >>>>>>>>>>>>>> 52A"C>Jn>>N*>2> $>n20>> 0"tpRh&>":"$>>>>>>$H>>>F>>>>:d6>6T>>>>N>&> $>D>^>.\>^>n>D>>>>>>>>>>>>>F> ,j> Dj>" \j~>>b>>b222$.4> >> >* j>> >r">>> 0 X @"@* h x >>>>N>>>>>>>> j6 j6 >N>> >>>F>>>> >>>F>>>> 0 @ ` P"P* pj6 RVV>"|>^" >>  H>^& &`> Z>>f&> V>>>>>> H>>>64>>>> ^VVV>>>>20Z>0Z>0Z>TZ>lZ>Z>0Z>0Z>Z>*Z>>Zn>>Z>>> Z>>>"Z &>>@Z>>>""XZ>>>22pZ>>>>>>>&2Zr>>>*Z>V>>2Zr>>>*Z>>>>&Z>>>>&(Z>>>"@Z>2XZ>2pZ>R>>>>>>>6f>~j>N>>>v>2$*>E"G >>>>>>>><>"\>>^^JN^Z>r>RRR>>r2(RVVl*|RJj>bn24fVv>XN6X<>Vv>N6v& n&  ^, 4&T>xZ~>d>>>:,>>Nj*>>*&6>>N>trf& >>>>>>>>>>>>>><>$ :$ t < < 4<< < | D> t>>>>>>EC>>>>>>>>>>>>>>>>>>>>>>>>: >>&D> >>>AI"K`^^>j>jj.>\R*$>> hJn>x>"$x2h&Dh>H&$HZ>>^f^Zj^Z^r>N^jvf6>b.\`lF>J.> ,Zf^z$"6|*<@hZ:JJ*>>>6>$2>,2>*>M"O>>>>>>4>>R:&>D:$>>>>>>>>>>>>>>>~ xV xV xV xV xV xV xV xV xV xV xV x x x x x$x$x$x$x$x$x,x,x4x4x<x<x<xDxDxDxDxLxt$^>b>N~>>&>,>>"\j2h>>V2h>>R6h>>>2h>>h>>> h>>>>>b2>>R>>:>>>>>*>>>>>>>>>>>Z>">:>>>>&<>>>>D.>>L>>>J>">>>>>>>>l.>>t>>>J>6>>>>>>>>>*:>>V>6>>>>>.>>.>>>>">>>>>.>>>>>>.>>>>>>>>>>.>>>>>.>>>>&>>>>>>>>.>>.>>N>><>>>>>>>.>>>>>R2>>R>>>>>>>>>>>>n2>>N>>>>>>>>>>*n2>>N>>>>>>>>>>*@:>>>>2H.>>>>>>>>>>2H.>>P>>">>>>>>>X.>>`>>>>6>>>>>>>>>>>>>2>>$>>>J>>>>>>>>>>D*>>L>>>J>>>>>>>>>>>l*>>t26>>>>>>>>>V.@>>Z2@>>R>"@>>>>2@>>>>@>>:@>>2@>>>@>0> <.0>>D0>lj.>>V2>>R6>>>2>> >D>>>>>>>b.>>Z2>>R>>>>>2>> >Dj.>>>>~.>>Z2>>R>6>>>>>>>.>>2>>Z>^z>&0>:<>d>>>>>b2x>>R6x>>>2x>>x x>Dj.>>V2>>R6 ><>>>N.>>R ><>>>>>>>>>b.8>>Z>8>>R>>8>>8>>>68>(>6T>>>N:x>>Rxx>Ln>>&$Z&>TFv> >"$>.>LZr>^*>"Q"S(>>>>>>>> L>>j>>(>NRR(4r(d>,>4.$>:$>&$Z>>>>>d.$.$>nnnnnnnnnn24Z>^>>F>F>F>F^^Z>>>>Vb>>^^Z>>>>Vb>^^>j>>>V>V>F>>r2>DJ>n>n&$vzzzz>b>V>NnN>nv>r>n&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>L>d ,Z^>>>>>>>>>.$$$*,Jfff$$$>N>F>F>F>F>F>F>F>F>F>F>Fnnnn>,~>F>F>F>F>D>D>4>b>f>F>>>>>>>>>>.Jn>F>>R4J>$>$>$>$>$>4~n>>>>R((Jnn>>R(>>>>>>>:QO>>>>>>>>>>>>>>>>>>>>>& >>>4>D $ t $>>>>:d>D>>*>6>*l>D>D>d>D><><> T:<*>>>>>>>>MK2U"W>>>>>>>>>>>>>>>> ԮV^^^^^^^^VNfffffff><>nP*P*P$&P,&P4&P<&PD&PL&Pt$*$Z6$*46,FVV^b>,$2$2$2$$:$Z> $>> >>>>>>>>>lz~~~~~~~~ t $$, $$ l $$$$ $$,>>>>>>>> UKY"[ >>>>>&\ZN^Z^Zf>Nv>4*$*,z2> >&4T>>>>>>>4$ $ $ T>>"YK>>>>>>>>>>>>>>>>>>: $>>I"]"_@8>Z>8J> PP> 8(N>L>d>fn4bn>rJ>8>"$bn8>F>8>"$bn8:V>F">JnZxfr>&,f>Zrb>Z:$br>>Z>^20>>>>>>>>>>>>>>>>>>>>>>>&d>>>>>r>^>.nr> &\.0>>>>>> >N>>>>"$>>2.4>.D2.T2.d2.t2> 0~>r>n>>fnv^VVV>  >>$>*4bjr>F>>>>>n>j~bj>f>X"$ p,>"X TRV:L2(>>.z>Z$>V>F>J>Jl>XZ^^xP"D>F>nj>F*>>>>>>>>>>>>>>>>>>>>>Tl>@>>]a"c >>>>>>>>>>>>>>>>>>>>>t>>>>>&<>>>>>>>>>>>>>>>>>>>>> >>>>>>>>D>>$>>>FRRTRRRRRRRR&> >^:p:pfRpT.,ZL>Vrr>Znnnnnnnnnnnn^bZ>>>>Rb>>>j>r>j>>r2@>*@r>>F>>>J >>>2>>>>>2>>6>><>>>>>>J2>>Z2>>R6>>>>>&<>>>~ >>N*P>4>>>>>~>>>>>>>>>>.$>>>>>$>>>>>>F>:L>>>R2>>R&>:>>>*>>.>>>>:> >>>>.>>>>:>> >>>N.@>>R>@>>>>@>>>>@>>>>$>@>>>>&,>@64@>\>>>N.>>Z2>>R6 ><>>>>>>J.>>R>">>>" >T>nZ&>&,F>@>>2@>>Z * >$jj> t>Zx >jj*x >2 x >>R6> > ,jj>Lf:df:|&>TR>&` >|R> r> f>0 >X jj*X 4&X \>J>Nd>z> ., >Z & >$fj>  >>R: 6,& :Lrv*h ,fj6 $>T>>n > $.h f>b0 >*0 $>  >`  >>>>>>>>>>>>>>>>>*>>>>>>>>>>>t>>,>>>>>>>>>>>>>>>>>>>,>>>>>D>$ T $ F>F>>R"04F>F>F>F>F>F>F>F>F>F>F>F>F>F>F>L~>F>>>>>>>R.`pFn>F>>Rp4>>><><.<>4>6>\> <> D>t>&P :\ 4>>>>>>>>>>*ae"gX8>>R88>>8D>>Rxf xx<>J>>V2T64Z>44$.$>>>>>z~~~~4 D < D4 4z:4 ,$$ $>>>>>>>e>i"k>>>>>>4*,Z>V^ &4"HRb^>RrrxJfrr>>>>b>&$~rr2L~rr>>>J2&>"~^Z^2>>>>>>>>> >>>4>L>6i>>>>>>>>>>>>>>>>>>>>>>>6RD t|2L*T6D**d L DFN>vVV>f>f>f>f>r>f>f>f>>>>>.4>>>:@>>>>2@>2@>2@>2@66@>>>>>>"newloaderloadprimrethrowsmakessetsgetsblitssubsfind@make@empty@consstringasizearrayamakeablitthrowasub@compare@print_unionnargsobjfieldsfieldobjgetinvalid_argintfloatprintgetposcacheNoneSomeNeko_errorInvalid_argumentAssert_failureErrorStream_errorNot_foundExit@print_record@aget@aset@pconsstream_tokenstream_posstream_junkstreamsprintfsndprintfordnstringnprintnekominmaxmagicignorefsterrorcomparechrassertsubsortsetmapmakelistlengthiteriiterinitindexcreateblitappendadd@rmaketlsplitrev_recrevphysnthnonememiter2hdfoldfindfilterexistsconcatchopassocallresetadd_subadd_charssizeuppercaseunserializeunescapeserializelowercaselist_depencyis_printableescape_charescapeOverflowEofClosedBlockedwrite_ui24write_ui16write_stringwrite_i8write_i32write_i16write_filewrite_charwrite_bytewritestdoutstdinstderrread_ui24read_ui16read_stringread_lineread_i32read_i16read_fileread_charread_byteread_bufread_allreadoutputinputflushfile_write_charfile_writefile_stdoutfile_stdinfile_stderrfile_read_charfile_readfile_outputfile_openfile_inputfile_flushfile_contentsfile_closecreate_outcreate_inclose_outclose_inhkeyhnewhcounthgethmemhaddhremovehsethiterreplaceremovehashEmptyMatchStarPlusNextChoiceInvalidRegexptransitionsstarsingleplusparseoptnodesnodenextmax_codemake_transmake_tablesis_emptyinvalidgroupescapeddeterminizecunioncintercemptycdiffccomplementcalladd_nodesadd_nodeInvalid_ruletokensourcepunionnull_poslineinc_lineemptydatacurrentcurposcharbuildTrueFalseNullThisIntFloatStringBuiltinIdentVarWhileDoIfElseFunctionReturnBreakContinueDefaultTryCatchSwitchSemicolonDotCommaArrowBraceOpenBraceCloseParentOpenParentCloseBracketOpenBracketCloseConstKeywordBinopCommentCommentLineNormalWhileDoWhileEConstEBlockEParenthesisEFieldECallEArrayEVarsEWhileEIfETryEFunctionEBinopEReturnEBreakEContinueENextEObjectELabelESwitchvar_argss_tokens_keywords_constantmk_stringmk_intmk_identmk_call1mk_call0mk_callmk_builtinmk_binopto_stringprint_listprint_astnewlinelevel_exprlevelparse_switchparse_stringparse_opparse_listparse_from_stringparse_fieldparse_expr_optparse_exprparse_constantheaderidivNoderemove_min_bindingmin_bindingmergeheightbalAccNullAccTrueAccFalseAccThisAccIntAccStackAccGlobalAccEnvAccFieldAccArrayAccIndexAccBuiltinSetStackSetGlobalSetEnvSetFieldSetArraySetIndexSetThisPushPopCallObjCallJumpJumpIfJumpIfNotTrapEndTrapRetMakeEnvMakeArrayBoolIsNullIsNotNullAddSubMultDivModShlShrUShrOrAndXorEqNeqGtGteLtLteNotTypeOfCompareHashNewJumpTableApplyAccStack0AccStack1AccIndex0AccIndex1PhysCompareTailCallLoopGlobalVarGlobalFunctionGlobalStringGlobalFloatGlobalDebugGlobalVersionInvalid_filewrite_debug_infostrap_stack_deltaread_debug_infosop_paraminullhash_fielddumpcode_tablesXEnvXStackXGlobalXFieldXIndexXArrayXThiswrite_optrapstack_deltaset_posscan_labelssave_breaksprocess_continuesprocess_breaksmake_arrayjmpgotoglobalget_cases_intserror_msgcompile_functioncompile_constantcompile_builtincompile_binopcompile_access_setcompile_access_getcompile_accesscompilecjmpcheck_stackcheck_breaksdocstackcdatapcdataxmldonePCDataCDataDocumentnode_textnode_nameis_pcdatais_nodeis_cdatafirstNodeattrib__listto_xml_recto_xmlparse_xmlparse_posInvalid_characterUnterminated_stringUnclosed_commentUnclosed_nxmlInvalid_escaped_characterInvalid_escapestrnxmlmk_floatmkkeywordsidentexprestringenxmlecommentcommentbinopUnexpectedUnclosedInvalid_nxmlvariables_nextvariablesswitch_casesprogrampriorityparameters_nextparametersparameter_namesobject_fieldsmake_binopexpr_nextblock1blockpathVNullVIntVFloatVBoolVStringVObjectVAbstractVFunctionVArrayvalueneko_valuemodule_set_globalmodule_read_pathmodule_readmodule_namemodule_loadermodule_globals_countmodule_get_globalmodule_exportsmodule_executemodule_code_sizeloader_pathasetaget__list_depacopycallstackexcstackCFunctionModulePosexcrunreportneko_pathlinkdo_linkTBaseTAbstractTCustomTFunTOptTStarTMultTObjTNamedTPolyTFunctionBeginEndQuestionDoubleDotPOpenPCloseBrOpenBrCloseFieldQuoteSharpDocInvalid_charUnknown_typeUnclosed_docInvalid_doctype_write_partype_writeto_htmlstatusmk_tokformat_xmldocumentationdocumentdoc_type_nextdoc_type_listdoc_type_fieldsdoc_type_basedoc_typedoc_tokendoc_optiondoc_docdoc_contentargsversionwithout_extensionwithout_dirset_cwdread_directoryput_envis_directoryget_envget_cwdextensionexitexecutable_patharray_dependencyVoidInvalidparse_argshelpverbosereleaseparse_multiformatoutis_nxmldircompleteL/`S6_L_LL9LLL!b L%b ^)^QLLLU^rL3LL bL!LLKVLcfI^LLLLKVLdfLL)L LKVLT^\LLLLKVLefLL)LLKVLT^0LL L LKVLffLL)LLKVLT^ILGLL9LLL%b L!b^zQLLXLF:^i)L3Lf)L!LLKVL9f)L1LXL0V^8^^)L1LLRL;LLL-L%L!L6VLH9R^9f iLL:VL=L)LL7VL)LjLVLLAVLL)L/VL41Rr9f lLL:VLLYVLLDLL-)LmL=VLLAVLL)LVVL4)R9f oLL:VL=LLpLVLLAVLL+L)L?VL4!RLLL!bLb^ ^sLZ^^sLZ^^9f rLL:VLLYVLLDLLL-L-tL)L*VLLjL=VLLAVLL-L-L/VL4AR9f vLL:VLLYVLL`L;LL-L-L%L!L6VLWL)L--LwL=VLLAVLL)L5VL41RLXL2VL!f8LLPL,V8LyL-LSV^!8LzL)LJLLL[zLSVL)rL3LL!fLL%LQVL/fLL%LQVL\f L|^L9L9L9L99LL=LT9LO9LLT9L9L9LLL!b L%b^$=L-L*^L>LLL%z^iL/`S7_LL9LLt! zLLLLLLL)LL/HoL#g%Zrrj9LLLL=Vl^$LLf LaL^L/*)r9L=j 9L=l^$LLf LaL^L/*L9LvLL=LvL)LLvzLf^L9VLL=!r9L=f LL=L9LVL-L-LL9LLV9L9L)r9L=f LL9LVL9L9L%!LLLLLLvLL-L-LLvzR9L=9L=L9LL=LvL)LLvL-LzLf^L9VLL=!r9L-L-L-LVr9LLLLLLL-LzLLLRLLzrLLL)LL)L!bL!b)Lb )L)Lf L-L-L-LCrL!f LL!fs^YLL!L)LL!f<)L)L)LVLL!f L)L-L)L)L)_)RrL!f LL!fs^LL!L)LjXL!fJLLL VLL!fL!f^LLL)LL)_l^FLLf LaLf$)L!f L-L!LLV^ ^L/*9RrLLLj"fLLL9VLV_Ll^/LLf LaLf )L^ ^L/*)RLLj5f%LLL f LLLV_LLVl^.LLf LaLUDf L^ ^L/*!L9LL9L)L9L-L9L-L-LL)LLLRL9LL9LLLRL9LL9L)L9L)L)LLLRL9LL9LLLLLL!f L^RLL!rLLQLLLF)LL-LzrL)rLLL)rLbLf LLLL9"r)LL)L!bL!b)Lb )L)Lf L-L-L-LCr!LLLL!f<)L)L)LVLL!f L)L-L)L)L)_RrLL9VLLL9VLLL9VLLL9"rL!bLf LLL9VLLL9"rLbLf LL!fLL)L9"^ LL9"rL!bLf  LLL9VLLL9VLLL9"rLULLL!rLLQL FL LLF)L)L)LVL9*r6L/`S7E_ L/jL!LLfLLL/%[VL%a_RrLL/ RVLLf %Lr)L)L)L/%[Vf 'L!rjLLLLL/(-Vl^Lf )L1r)L)L)L/2[VLLf +Lr)L)L)L/,VLLf  LrLL"VLLL#.Z!rrLLL!b L%b^P^MUL!fQL9LLVR^4^^QL)UL9L)LV9L=LVL1^LLLL)LL-L/va)LL)RrL fL~LLL b$L b%L b&L\b'L"b(^.^*2^X^"3^P^4^H^5^@^ 6^8^LL9f7LL!LV^^^L%LL=*L9L=f L^9LLVLLL b$L b-L b6L\b?L"bH^V^RL2LV^^BL3LV^q^2L4LV^a^"L5LV^Q^L6LV^A^LLfL7L)L!LVLV^^^LLLVL%LLLLL-L9LL=LLL9va!L!RL9LL=L!L!LLg< L)L/ RVLL\g)L-L%)Lf ;LL-L/ RVLLu^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^_]____Q____E^v^t^r^p^n^l^j^h^f^d^b^`^^^\^Z^X^V^T^R^P^N^L^J^H^F^D^B^@^>^<^:^8^6^4_^0^.^,^*^(^&^$^"^ ^^^^^^^^_^ ^ ^_^___LL"L/%[V__LLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR_ _LLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR_K_BLLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR__LLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR__LLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR__LLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR_O_FLLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR__LLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR__LLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR__ LLf ;LLL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f ;L0L!LL!LLdL-L!L)L L)L!L)LLf ;LLL)LL/%[VR^S^JLL\L/%[V^A^8LL L/%[V^/^&LL L/%[V^^LL L/%[V^ ^;L^^^LL-L)L/%[VLL%L)L%_)L!L)LCRL9LL!L)L=VL!LaL!LAL!LLfHL)L/ RVLLAfLZf-L-L)L!LLL/%[V)L-L%_)RL9LL!L)L=VL!LAL!LaL!LLfHL)L/ RVLLafLzf-L-L)L!LLL/%[V)L-L%_)RL?L/HoLAE7L/`S7z_fH#MRLLTLHL#1iZfrL)L)LHL#1iZLV"rrLH#MRLVLLL!b L%b ^!^UL%LL9𭈥^LLL!b L%b^ZLR^ QL^LLL!b L%b^\LR!^ UL^LLL!b L%b ^0P^,QL)ULIL)LLL-L9VL%z^rLLL!b L%b^$^!QL)ULLLL9B^rLLL!b L%b"^y)LL!f^ ^^`LR9^\QL)ULLLL!b L%b^2`LRQ^)QL)ULL)L VLL)L9s^^rLLL!b L%b^PPLP^GQL)QL-ULL9LLLILL)L%zLILLL%zR^LLL!b L%b^)^&QL)ULLf^ LL9B^rLLL!b L%b^0^-QL)ULLLMVL!f^ LL9B^rLLL!b L%b^@SLO^7QL)QL-ULL-LMVL!f^ LL9^rLLL!b L%b^<SLO^3QL)QL-ULL-L!f^ LL9^rLLL!b L%b^/SLO^&QL)ULLf^ LL9^rLLL!b L%b ^*)^&QL)ULLIL-LL%zL9B^rLPL9LLL!b L%b ^+-^'QL)ULLL-LL)L9^rLLL!b L%b ^+)^'QL)ULIL)L)LL9VL%z^rLLL!b L%b ^')^#QL)ULLL-L9VL=B^rLL9L)LlvaLPL)"L!f^;LLL!b L%b^!nLR)^ULL%LL9:^rLLL!b L%b^)^&QL)ULLfLL9B^^rLLL!b L%b^)^&QL)ULLf^ LL9B^rLLL!b L%b^3rLR^*QL)ULL!f^LL%L9^rLLLsaL!f tLR)L)L)rLLL!b L%b ^?P^;QL)ULLfIL)LL-L9VL%z^ LL9B^rLLL!b L%b^/^,QL)UL9LL-LKVL%LL=B^rLLL!b L%b ^OLN!^GQL)UL%LL9L)LJVLLL)L)Lwva%L-L)VR^L9L)LLQVLL!rz7FL/`S7_LL)QL!LL)f.-L-L)$L|L#(02L%aL)f -L2_-L2-R9LL/1&L)zQ9L!L/m%zL)QLL/1&Lf,LL%L/~4"eL!LL!LL/}V-L)L)L@-LL%FRrLL"r)L)L)L}3rL/~4"L!LLfL)L@L%a_9LL-zRrL/~4"L!LLfL)L)L@L%a_9LL-zRrQL!L)2L)f)L$LL%a_RrQL!L)2L)fL-L)$LVL%a_RrQL!LL/~4"L-2L-fLLL$L@L%e_9L-L)zRrLQL-L+rQL|#MRL)2L!fL%e)L)$LL|L#1iZa_RQL!L-2L)f)LL)$L9VL%a_Rr)QL)L)Lf L/&]9L)LLLL/[uVzrLLLLL/}UrQL)2!LL)f$-L$L-L|L#eZL!frL%a_RL~r7{L/`S7_LLL!b L%b^ L-LL#KZ^L-LL#KZ^9LL%zLLL#KZ9LL9e*zLLL#KZ9LL% zLLL#KZ9L)L)LvzrrLLL#KZ9LL5/zLLL#KZLLL#KZLLL#KZLflLLf L/\L%f LrL)L/1&L!LL)f+)LL)$L2L%aL)f )L2_)L2)rRL/(0 L/mLL!L)L/1&L%LLfL-L)$LL/LLf )L%2^!LLL L-L/AVLL2-L%2R_)L2)RL/1&LLf)rL-L2L)f+-L-L)$L2L%aL)f -L2_-L2-rRr)L)L!f!r)Lf)L/1&L)L/1&LLfLr)Lf!LfLf LrQa_!LLf4L$LL)$LLLzLVLL!frL%e_!rR)LfLf!LfL-f -L-rQa_-L/mL!LL/1&LLfHL-L-$L/AVLLL$L/AVLLLL zLVLL!fr -L%2R_!rR)L)rrLLL+rL!b L)Qf LL#eK3L7L?O17LL7L?7.LL7L?%LL7L?x+LL7L?;WLL657L?LLw 7L?LLUD7L?>#-LL/HoL#g%Z77L?pL?K7L?eL?SL?!nL!777L?!|L?1iL?MRLLLLLL)LLLLLLLLLLLLLLLLLLLLLLLvLLLLLLLL?&]L ?(0L-?>9L?!L)?'}L?-L?L(L?iWL?a1L ?-XL ?TL ?/iL?L?yL?SL? SL?-qL?eK<L? PL?L?ML?ŖL?)L?iL ?-zKL?FR 7||#!n7}|#&]7~|#S7|#7LL7LL/HoL#g%Z7L?.Z!LLLLLLLLLLLLLLLLLL LLLLL-?WL?(0L?^'XL ?WL? SL ?9VL?^L?L?Q L?ſL?;L?>L ?NL ?fL)?58L?L ?!IR7G7HH#!|7IG#9V7JH#!n7KG#^7LH#e7MG#f7NH#&]7OH#MR7PG#^'X7QH#eK<7RH#7SU7TW7VzLX? szLT?.Z!LLLYa[L]LLLLLLLLLLLLL^2 LL_2LLa2LLb2LLc2LLd2-LLe-2)L-Lf)2L)LgeLLhaLiLLLLLLLLLj2LLk2-LLm-2)L-Lo)2L)LpeLLqauLLLLvaLxLLyLzL?xezL?nzL?^'XzL ?4zL ?cVzL ?9WzL?bSzL? zL?SzL? SzL?zL? zL?ſzL?ZzL?\zL ?9лzL-?5zL??zL? zL?kzL ?r9&zL)?m%zL?zL?!IRzz7L/`S7 ^LL"rL 77L!L/HoL#g%Z7LL/HoL#g%Z7LL/HoL#g%Z7LL/HoL#g%Z7L%L/HoL#g%Z7L%L/HoL#g%Z7LLLLLL L-?(0 L?I L?f L)?" L?0 L?!IR  77#ſ7#(07#L(7#07#&]7#!I7#f7#-zK7#eK<7#7 #T7!#LL/HoL#g%Z7"$L/jL/VhL&L(L*L,L-LL.L0L1LL L8vLLL-L:vL L LL<vL LL=vL LL>vL@L%L/HoL#g%Z7?BLL/HoL#g%Z7ACLDLEL-?>EL?gEL?r!EL ?WEL?nEL ?WEL)?]EL?9VEL?1EL ?,(EL?EL?r EL?NEL ?9лEL?EL?CEL?fEL? EL ?58REE6 66#K6#586#06#f6#W6#F6#T6#>#-6#"6#7.6#(06#L(6#&]6#6#N6#!I6#f6#-q6#-zK6#S6#eK<6LL6L?bLL6L?4LL^!6L?LLlq 6L?̢O6LLVLL%LVLL%LVLLLVLL%LVLLLVLLLVLL%LVLLLLvLLLvLLLLLLvL LLvLLL!LVLL!LVLL!LVL)LL)LL)LLLLLLLLLLLLLLLLLLLLLLLLLLL-LLLLLLL L LLLLL?uKL?tKL?qL?O$L?L?L ?uaL ?veL ?L?lYL?˿L?H L?&L?p>L?p>L"?ڥAL?~8L?9L ?9L#?Edž4L?W2L?1L?4L?x3L?VKL?a1L? L? >L-?bL&?L'?0L?n T<L?+L?EqL<L(?L)?9L!?YL,?OL$?L%?}r*L+?}~&L*?u2:L ?zL?L)?G9L ?LLR2mL/`S7J_O !LL)zL LVLFL)L)f%^L9rQfLLL LVLVLFLLLL!b L%b^X^UUL!fQLLR^?^^QL)ULL!LLL9VLLL-L=d^r!LL-L9"rL9=Qf=L%LV=LLL!LL9L=VLLL!b L%b^^QL9LLVL9^RLLL!b L%b^<LL*^UL!f3QL)QL=L)L9L=VL9LR^Q^^QL)QL-UL=L-L9L=VL9LLV9LL9^LLL9=LALV=LLVL9=LL*RLLLL9LL^)^B^^__6_b__ ___$____'_P___wQL!LLLL=R_\QLL$LVLLV)LLLL&vLLVLL%LLVLI_ QLQf"LLVL-L'LV_^^QLL(LVL-L)LR_QLULL*LLL=Z_QLULLLLVL+LL-LVLLZ_`QLULLLLVLLLZ_2QLL,LVLLL=LLL-vL-LVL%LVLI_*LL!b L%b0^iQL)ULL.LVLLLLk^>QL)ULL/LVLLLVL.LVLLY^_pQLUL)*LL0LV)LL)L)LLVL!LVLLL!b L%b^#^ QL L1LV LLL^_QLUL)*LL2LVL-LLV3L)L L=VLLLk_QLULL4LVL+L!L L=LLVLLVLLLc_fQLUL)*LQLffLLV^ L$LVLLL9V5LL L=VLL-L9VffLLj^ LLj_QLL!b L%b^(L6LR^QQLL7LVLQ^_QLL!b L%b^(L8LR^QQLL9LVLQ^_gL:LJ_[QLULLL%LVLLQ_0QL!fL;LV_^^QLL$LVLLVLLL=LLLLL>vaLLLVLLZ^QL?LLL=R^QLUL)*LL@LV)LL$LVLLLLLBvL)LVLLL!b L%b^/^,QL LCLV LLVL LLV^LLVLY^RrL)LLLL%bLb/^{^wQf LEL9VLLLL=V^^PQLQf LEL9VL$L9VLLVLL)L=VLLVLL9VR^9^LLVLLLL=V-f L%L9VLLBRr9LL)L=V9Qf9L%LV9LLLL%f%QL-L9L=LLGvLLV^^^)LL-L93rLLLLL9LLL=V)LL)1RJ7 7 z7 E7 L/`S7_8 L ^^^)^3^=^G^Q^[^e^nYL)LR"^bZL)LR"^V[L)LR"^J\L)LR"^>]L)LR"^2^L)LR"^&L)LR"^_L)LR"^`L)LR"^9LLz9LLz9LLz9LLz9LLzL ^^'^1^;^E^O^Y^c^m^w^^^^pL)LR"^qL)LR"^rL)LR"^zsL)LR"^ntL)LR"^buL)LR"^VvL)LR"^JwL)LR"^>xL)LR"^2yL)LR"^&zL)LR"^{L)LR"^|L)LR"^L^#^-^7^A^K^U^_^i^s^}^^^^^^^L)LR"^L)LR"^L)LR"^L)LR"^L)LR"^L)LR"^zL)LR"^nL)LR"^bL)LR"^VL)LR"^JL)LR"^>L)LR"^2L)LR"^&L)LR"^L)LR"^L)LR"^9LL z9LL z9LL z9LLz9LLzLLL!b L%b^L-LR*^L-LR*^L^)^3^=^G^Q^[^e^o^y^^^^^^^^^^^L)LR"^L)LR"^L)LR"^L)LR"^L)LR"^L)LR"^L)LR"^L)LR"^L)LR"^zL)LR"^nL)LR"^bL)LR"^VL)LR"^JL)LR"^>L)LR"^2L)LR"^&L)LR"^L)LR"^L)LR"^9LL!z9LL%z9LLz9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9LLz9L-L-L-Lzrr9L-L-L-Lzrr9L-L-L-L zrr9L)L)L zrr9L-L-L-L zrr9LL z9LL z9L)L)Lzrr9LLz9LLz9L-L-L-LzrrLL-L-LVrL)LVLVrL-LPLLVL%zLVrL)LnLrL)LlLrL)LfLrL)LjLrLLLLVrLLLLL!b L%b ^T^QLL9LQ^LRLLL9LRLLL9LL9RLLLL^)^+^?^P^i^^^^_F_p_____!_?_X_Z_)_QLLLXVL_QLLL_QLULLLLV_sQLULLLL)LXVLV_OQLULLLLLV_/QLLLLXVL_QLUL)*L)LL)LL)LV_QLUL)*L)LL)LL)LLL!b L%b ^T^QLL LQ^LV_QLUL)*L)LL)L)L LV_`QLULLLLV_EQLUL)*L)L)LL)L LV_QLL%b L!b^ QQLLLQL^-^^QLL%b L!b^ QQLLLQL^-^^)^QLULLLLLV^QLLLLXVL^j)^fQLUL)*L)LLLL-LXVL)LLL!b L%b ^T^QLL LQ^LV^RrLLLL!b L%b^^QLL91^LL9LLL9L9!RLLL^)^*^9^E^W^r^^^___%_=_h_______QLLLSB_QLL9_QLULLA_QLULLLLSJ_QLULLLA_QLLLLSB_QLULLLA_wQLUL)*L)LLLLL!b L%b^^QLL a^_*QL*LLLA_ULL9_UL*LLLA^QLL%b L!b^QQLLA^^^QLL%b L!b^QQLLA^^^^QLULLLA^qQLLLLSB^[^XQLUL)*L)LLL)LSVLLL!b L%b^^QLL a^^RrL ^^^^^^)^0^D^O^U^Q^M^I^EQLLW!^7QL^.QLLLUL^QLL숥^ QL^L ^^^!^#^%^'^)^+^-^/^1^3^5^6^2^.^* ^&1^" ^ ^ ^:^ ^2^ ^^L^#^%^'^)^+^-^/^1^3^5^7^9^E^Q^X^g^q^m%^i^e+^a^]$^Y^U^Q^M^I^EQLL9!^7QLL=!^)QL^ QLLL^QLL숥^7Kz7LL/`S7S_9LL!9LLr9zL)QL)QL6VL-UL-UL+VLzrLsLL$L)L!LL!LLsL/L=L!L!LL z LrL!FL!FLUFLUFL-FLF L)F LF r*L* L)L1L)* L-*z*L!f*LUfVULL$LL)UL-QL!LUL"VL)*L-UFL)*L-UFLFL)ULF*L*LQL!L-QL*LL"VLFL!F*L)QL)LULL#VL)L-*L)F)L-*L)FRQL*L2VLL)*L%FL)*L%FL f LF ^L b* fLF L)*L%F rj8L9LL*L%FL*L%FLL=VLl^*LLf L-aLf%^ ^L/*9L$L=L$LL)L*VLLL)LfLLLf ,L3-L%L)L2Rr9L9LLf6=Ls)f&=L=*LL%F=L=*LL%F^=L=*L-F=L=*F=L=QL=*LL'V=L=*L-F=L=*F=L=* L-F !LL-f"=L=L)L2VLVLL%_RL)LL0LL!LL!LLLLL9LLLLLJvaLLL=LKvLj!L!LVLLL(Vl^nLLf L-aLLb LUDb6^?LfLL%LLL*VL-^^L-^ ^L/*LLf LQ^LL)L*VRrLj L4l^(LLf L-aL;L3^L/*!NL)L8VL0L-L8VL LL-L-L L.L&zRrPL7QL5LL9"S77z7E7L/`S7_L^^^#^-^7^A^JL)L]"^>L)L]"^2L)L]"^&L)L]"^L)L]"^L)L]"^9LL%z9LLz9LLz9L)L)Lzrr9L)L)LzrrLLkVL!L)LL!f^^^L!f)^^^L%fL%fQLLL-ULQLQLULL-fWL%L-fxLLL L9VL%z^1L)f-LxL-LL-L%zL9V^ LL9V^ LL9VR^"^^^^LGLb*rLLL!b L%b%^]-L9fxL9LLL%z^^=QL)QL-ULxL-L%LL-L%L-L=VL%z^rLL9L)LvaLL%f/QLf!QLULL%LLVR^^^^^LLL2L9LL9L=VL9rL9LL=VL9rLLL-zL9L!LL)LoVf^xLL-L%zLQL9"r9L)L)Lr+r9L9L%9L^^^7^n^^__QL9L=LLxLLLL%z襥^QL9L=LLLVLLxL)LxLLL%zL%zFR^QL9L=LLLVLLxL)LxLLL%zL%zFR^iQLULLLLVL:^IQLUL9L=LLxLLLVLxLLLVLL%zL%zF襥^r9L=LL)L)LV!LLLLL)L9L-LvaL9L)LvLL_2RLL%fUL%fxQLQL)ULQLQL)ULL)L!f$xLLLL9VL)L%zL=^xLLLL=L%zR^^^^^LL-LLLRrLLxL9L-L%zLL=LVRLLLL9L=VRLLL9RLL-LLxLxLLL%zLL-L9VLLL=LvLLVLL9LvLLVLVL%zLLLLeVL)LLVRrLLL)L9VLRLL-LLL-LhBRrLL9L)LvaL)LVLyLLL{VL)LLL=LLL9LvaLLL-LrVLLLLLLVLL[LLLcV|LL_VLfL)L_VR j9LL`Vl^[LLf LzaLw f9=L=L=L%9L-L)LtVLxLLL%z^ ^L/*LL9Ld"LLVLj9LL`Vl^LLf LzaLw fx=L=L=L%9L-L)LtV-LLLLLLL)L_VLLLL_VLLxL-L-LzLLL%zR^ ^L/*!L9L9L)L=VL9LwLL9LxL-L=LzL9L%zR9L)LvLL^"rL)LL)Lh2RrLLL)LiVLL{"L9LLLLL!L!LLLL-L-LvLLLLL=LLL LLvaLLLvLLlVL)LsLLLpVLLLLgLVL[LL-R QLL)LLL-f#9LL=LLuVLjVLL%_RLL\VLL)LvL-L^VrLL}f^L)LuVf^L%LL9"rQL!LL9"LL9LLuVL)L=*RLLLLLaLL)L_VL-L9LvL-L_VLLRRLL]9LL1.zLvLxLL)LL%zLLxL)LvL)LvLL%zRLLVL9L=L)Lr+LL~LLmVLL)L%LLL\b2L+b3L*b4L?b5L[b6L]b7L-b8^>^:)^=^2)^5^*)^-^")^%^)^^)^^ )^ ^-L9)rLLf"QLULLL9LVR^^^LLLf"QLULLL9LVR^^^LLLf"QLULLL9LVR^^^LL"LL!f^^^)L)L*r9L=LmVL=L=L%L]fLaLkVL!f 9L_L-fLaLkVL!f 9LLLL!b L%b^VxL-LLL%zL)^+r)L)L)L/"3rLL#eL/@+r)L)L)L#eL/:37aZ#F7bX#^'X7cU#S7dU#57eZ#M7fU#cV7gZ#i7hX#Q 7iZ#!n7jZ#e7kX#ſ7lY#N7mY#7nU#?7oX#;7pZ#O177qU#\7rZ#7sV#!I7tZ#S7uZ#T7vU#r9&7wZ#!|7xU# 7yZ#7.7zU#^'X7{Z#iW7|X#7}Z#&]7~Z#MR7U# S7U#7V#f7LL!7TL?mL7TL?%PL7TL?57L7TL?zk35L7TL?3L7TL?3DLLxL)L!LL%zLLLLLaLLL)LvLL-LvLLLLLLeL)La)LLLLL L LLvLLLLvLLLLLL7TL?rL LLvLLLLLLL)L-L)2L)LeLLaL-L-L-LLLL LLLLv LL ?>UL?[L?f!9L?Z?ZL?3U=L-?3TL ?1L? L)?L? L ??B/L ?fL?8L?G;L??L? $L ?z2L?<<L?wuL?J.L?L? L?L?S5L? JR77#O177#m%7 #K7!#587"# >7##f7$#>37%#f7&#W7'#F7(#M7)#S7*# S7+#>#-7,#7.7-#z27.#ڥA7/#iW70#71#N72#&]73#3U=74#MR75#S76#eK<77# S78:LL<7;L;?!L!L!L=zL>L?L@LALBLCLDLELFLGLHLLLIvL)L)LMvLOLLRLSL-?+SL ?۰12SL?WSL?VKSL ?SL?1SL ?SL? >SL?5SL?:SL ?*VcSL?9SL?H>SL ?fSL?^SL)?.:RSS7ME7N7OO#!|7PO#O177QO#K7RL#ſ7SO#>37TN#C7UO#MR7VO#(07WL# S7XaLL!7bKLb?n7L%7cKLc?|L7dKLd?3L7eKLe?~27Lg7fKLf?7Li7hKLh?|5Lk7jKLj?Lm7lKLl?*=Lo7nKLn?вʥ}LL!7~KL~?ǖAL%7KL?L7KL?;L7KL??L7KL?-L7KL?8gL7KL?=L7KL?L7KL?z L 7KL?L 7KL?!@L 7KL?NL 7KL? LL!7KL?4L%7KL?L7KL?i3L7KL?L7KL?8L7KL?b L7KL?e9L7KL?4wL7KL?"P=L 7KL?)L 7KL?L7KL?CiL7KL?nN L7KL?-L7KL??L7KL?q&LL!7KL?!;L%7KL?L;ʥLL7KL?N+L7KL?L7KL??L7KL?5`*L7KL?K9L7KL?\L7KL?^L7KL?,L7KL?4L7KL?-L7KL?}L7KL?#oL7KL? L7KL?:zL7KL?L7KL?طL7KL?L7KL?oc|L7KL?V/LLLLLLLLLLLLLLLLvLL ?d'L?"7>L)?':L-?L ?]UL?Ң8L?NL?SL ?L ?L ?_`L?I4L?ǚL? SL?ſR77 #ve7 #ſ7 #q7 #9V7#e7#>37 #G97 #lY7#7#F7 #a17#M7LLLL LLL)L)L"vaL#LLLL)LLLL LLLDveL-L-LLFvaL-LLHvLLLIvLJL?UJJL?a1JL?)JL?4JL)?-XJL?T JL-?JL?cJL?fR JJ6z6L/`S7C_L L"rLLL!f$LL)LLVL-LLVR^L)L L)L"L!f^()L)L LLLLL%L9VL%zrLL ^^^ ^$^(^6^D^Q^^^k^x^v^^p^z^j ^t^d^n^^LL^^^NLL^N^>L9L^?^/L9L^0^ L9L^!^L9L^^LLL!LL!^F^J^N^R^V^Z^^^b^f^j^n^r^v^z^~^^^^^^^^^^^^^^^^^^^^^^^^|^^^^^^^^^^^^^^ ^^!^^"^^#^^z$^^t%^~^n&^x^h'^r^b(^l^\)^f^V*^`^P+^Z^J,^T^D-^N^>.^H^8/^B^20^<^,1^6^&2^0^ 3^*^4^$^5^^6^^7^^L8LL!L9LL=LL9LL9LL)RL9L)L9L;L-L=VL-LL)L)L)LCRrLL!f^L9L9L=L9LLL)RLLLfvLLLLLVL_'_L=LL=LLLLVR__L=LL=LLLLVR__L=LL=LLLLLVR__L=LL9LLLL=LVR__L9LLLVLLL=LV_h_XLLL=LL=L)L)L)LVR_;_+L_0_ L=LL__ L_^L=LL^^^^L=LL=LLLVR^^LLLLVL^^LLLLVL^^L9L^^{LLLV^w^gLLLV^c^SLLLVLLLVL^<^,L=LLL=LLLVL V^^L?LLLLL-zLLLVL9f ALL=!rC77S7777#17#-7#>37#f7#F7#*=7#p>7#n77#N+7#M7#97#7#V/7#}7#VK7#7#!;7#L;7#7#p>7#^7# 7#N7##o7#oc|7#в7#7#ط7#O177#!I7#\7#,7#47#|7#77#~277#|57#?7#37 #!|7 #5`*7 #7 #K97 #MR7#:z7#)7LLLLLLaLL9LLLLL-LL)L:v-2)LLLL<v)2LL=eLL)LLLLLLLL@v aLLBvLCL?yCL?f0 CL?ȂCL?*xCL ?fCL?n#CL-?e#CL)? CL?0 CL?3U=CL? R CC6L/`S7\_.=L^^^%^/^9^C^M^VL)Lo"^JL)Lo"^>L)Lo"^2L)Lo"^&L)Lo"^L)Lo"^L)Lo"^9LL!z9LL%z9LLz9LLz9LLzLLo9L)L)LZg6zrrLLVLrLA^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^______ _ __________ _"_$_&_(_*_,_7_9_;_=_?_A_L_M!_I!_E!_A!_=!_9!_5!_1!_-!_)_%!_!!_!_!_!__ _ _!_%^QL!L쌥^QL!L쌥^QL!LL%^!^!^!^R^!LR^!^QL!L쌥^QL!L쌥^!^!^!^^^^^^|^x^t^p^l^h^d^`^\^X^T^P^L!^H!^D^@!^^`^u^_6_J_x___O__>_S_h_}___e_LLL9L)L:_*LLLL9L-LVLF_lLLL9L)L:_ULLL9L)L:_?QL!g`QQLgOQQQLfUL%fUQLUULLf:LLLL9VLL9LvLLVL*L)LmF^8LL9LvLLVLLLL9VL*L)LmFR_^^^^QQQLULLfLLL9LLVR_P^R^QQQLf\UL%fNUQLUULLL9LvLLVLLLL9VL*L)LmFR_^^^^ ^^^^QLULLL9L vLLVLLLL9VL*L)LmF_LLL9L)L:_zQL-f L-LeL=VLL9L vLLB_JLLL9L)L:_3LLL9L)L:_QL*LL*LRFLL*L*L%zF LLLL9VL*LRFL*LLL!b L%b^ L?LqV^ UL^F L*L%FLLL-L9VL*L%F_mQLULLmL*LLjF L*L-FLLLL9VL*L-FLF R_QL(fUL*LLLL a)LLfL*L%FLLLL9VL*L%FLLLL9VL*L)f^%FRR_^^LLL9L)L:_zLLL9L)L:_cLLL9L)L:_LLLL9L)L:_5QLULLLLL9VLLL-L9\_QLL*LFLL9LvLLVL*LF_QLfL-LeL=V_^^QL*LLVfLL-LeL=V_^^QL*LTL*LLjLzL*L)L)L}S_GUL%frUUL!fbQLUQL)UQL-*LLeLLLL-LL"LLLVLLLVL9VR^ۥ^^^^QLUL)*LLLLL9VjLL!l^*LLf LcaLUDf%^ ^L/*LL*L)FLLL9LvL-LVLLL!b L%b^^QL L LL-L9V^L*L)F^rL ^^^)^3^=^Q^s^__-LL92_-LL92_-LL92_-LL92_QLLLpL9:_zQLLL)LSL=VLzL9:_VQLLL)LgL=VLzL9:_2QLLLLb@LbNLb\LbjLbxLbLbLbLb^^L!LpL9V^^L%LpL9V^^LLpL9V^^LLpL9V^^kLLpL9V^l^VLLpL9V^W^ALLpL9V^B^,LLpL9V^-^LLpL9V^^LLLL9J_QLjULLVLL *fuj*L LVl^QLLf LcaLw f/ *L L *L%F L *L L-LVF^ ^L/*L LLhL9V^0 *LL LL!f^L%f^LL9Vl^HLLf LcaLw f&L)LL=VLLLzL9V^ ^L/*9^rLL^^ ^_ ^_4__QLLbLb_^QQLjULLVLL *fmj*L LVl^QLLf LcaLw f/ *L L *L%F L *L L-LVF^ ^L/*LL^Ll^@LLf LcaLw fL)LL9VLL^ ^L/*R^^ ^^^QLULLL-L=VLLVLR^^UL!fGUQLf6QLUQQLLL-L=VLLVLR^_^^^^QLULLL)L=VLLVLL-L=VLLVR^^L)LeL*rL^^%^@^T^h^|^^QL-LLL92^yQL-L*L)LL92^\QL-LL[L92^FQL-LLL92^0QL-LLQL92^)LL9*^)LiL9*^rL^^%^@^T^h^|^^QL-LLhL92^QL-L*L)LL92^uQL-LLzL92^_QL-LLL92^IQL-LLL92^3)LL9V)LLL9V)LL9*^)LL9*^rLLLbxLbL|bLbLbLbLbLbLbL bL!bL"bL#bL$bL%bL&bL'c__LL9V__LL9V_^L{L9V^^LL9V^^LUL9V^^LL9V^^LL9V^^LL9V^^LaL9V^^LL9V^^rL~L9V^p^bLL9V^`^RL]L9V^P^BLL9V^@^2LL9V^0^"LL9V^ ^L\L9V^^!L)L=2r-LLL(bqL)bL*bL+bL,cL-cL.coL/cL0cL1cqL2cL3cL4csL5cL6cL7cu__LL9VLLLL=VLLV__LLL=VLLLLLL=VL쭥_R_qLLL=VLLLLLL=VL쭥_ _?LLV*LLL9VLLLVL*L-LLVLLVLLL=VLL!LLYLLrVLLVLLVL!LLVL%LuLVR__LLV*LLL9VLLLVL*L-LLVLLVLLL=VLL!LLYLLrVLLVLLVL!LLVL%LuLVR__LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV__LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV_(_GLL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV__LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV_n_LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV__0LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV__LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV_W_vLL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV__LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV_^LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV_@^_LL9VLLLVLLVLLL=VLL!LLYL%LrVLLVLLV_^-L-LzLLL"bL#bLb__QL!f0QQLfLLL=VLLV_^^^^L!f1QLfLLL=VLLV_H^_^_ _QL!f0QQLfLLL=VLLV_^^^^L!f1QLfLLL=VLLV^^^^^^L!f}QLfkQQL!fXQL!fGQQLf4QQQLLLL!LLLL=VR^W^^^^^^^^ ^^^LLL=VLLVLLL=VLLL[r9L9*L%F9L9UL)L9*LVF9LL-L+r9LLwL=LeLV9LL)LLLULLL!L*L*L!L*LjLjLjLLL * L* LzLL$L-LV*LLLL9VL)*L-*L|L=VLLLeLVLULL)LL* L-LLmLQzLQL%zF)ULLLVLxV)*L!fZ)*LsLnVLL%L*LVLLL=L&vLLZVL)LzL=VL*LXL=J^LLzL=BRrL)LL%fSL%fFQLUL)QL-UL-L)LVL!f)LL9VR^^R^^^^^L-r9*Lf9L9*L)LuL=V9LL=LRLL9*f9L9*L)LuL=V9*Lf9LL=V_9LL9LL=V9L4LPLLV9L%LL=V9LL)LLL!b L%b^iLLTLjL%z^UQL)ULL9f"LLTLLjL!L=VL%z^LL-LL%zLL%L=S^r9LL=V9LL)L#9LL)L=V9LL9LL)L=V9LL9LL)L=V9LLL-LLL(bGL)bL*bL+bL,c;L-cxL.cL/cZLcLc_s_oL%f7UL!f&QLLL)L9VLVL=VR_^_5^_/_+L%f7UL!f&QLLL)L9VLfL=VR_Y^_^__L%f7UL!f&QLLL)L9VLL=VR_^_^__L%f7UL!f&QLLL)L9VLL=VR_^_i^_c__L%f7UL!f&QLLL)L9VLtL=VR_^_%^__L%fkUL%fZUUL!fGQL)UQLLL-L9VLL=VLL)L9VLL=VR_^_^_^__L%fkUL%fZUUL!fGQL)UQLLL-L9VLL=VLL)L9VLyL=VR_^_;^_5^_/_+L%gQL!gQQLgrUL!gcQQQLj *LLVl^6LLf LcaLw f0L)LLV^ ^L/*L*LLLL1a*LTL-L)VLLL L=L2vL)LTLV L=LL3vLLVLQL *f L *LQLuL=VQL *f LL=V_ LFULLL!b L%b ^3LLLL *L%zF^QLLLlL=V^ L=L LLL5vLLVRR_^^^^ ^^^^6L-LV__~L%g QL)ULLmLLxL *fLfoLLLL)L)L7vaLLLLLL%zLjL!LVLVLVL L L8LLLjL%zL LVR^mLf4LLL9VL=L9L9vL)LVLLL=V^2L9L=L:vL)LVLLL9VLLL=VR^ӥ^^i^eL%fVQL)ULL9L=L;vLLVLL-L9VLmLL!fLLWL=VR^p^^^L9L=L<vL-LVLLPL-LVf#L-LmL*L*LVL=B^L-LmLL=BrLLL!b L%b^ L Lq*^ULL!bL%b#^z^v)QL9L=L)LVR^^[)UQLfG)UUL!f4)QL-UQL9L=L-LV9L=L)LVR^;^^ ^^^QL)UL9LL-LVL1^9LL)L=V9LL9LL)L=V9LLLLLLL!b L%b^#9LL=V^QL9LL)LV^9LL=V9L9UL)L9*LVFR9*Lf9L9*L)LuL=V9LL=9L!f69L9L%=*Lf=L=*L)LuLV=LL9L!f69L9L%=*Lf=L=*L)LuLV=LLLL9LL=V9LL-LV9LLL=V9LL=*RLLL9LL-LL3RL9L=LLLLVL!fL!RLLLLL%b L!b(^)QLL9L=L)LV9L^N^RLLL9LL=V9LL=V9LL)LV9LL=VLL9LRLLL9L=LLLLjL%zLLV9LRLLLL)L9VL^)^;^^^__h___'___M__ _ _" _ _ _ QLLLL=S_ QL!fLLV_ ^^QLUL*LLLLLLL>va-LL*fL*L-LuLVL)LLVL-FR_t QLLL)LS_^ QLULLL-LVLLLR_5 UL%gUUL%gUUUL%g{UUUUL%ggUUUUUL%gQUUUUUUL%g9QLUQL)UUQL-UUUQLUUUUQLUUUUUQLUUUUUUQLUUUUUUULLL!f&QLfQQLf^^^ ^^^^gL?LPLL L LLPLLLLL LL LL LL LL L L%zL%zL%zL%zL%zL%zLVLLLfQLUL)LULL@LwLLLLLLLL@LjL%zLLVLLLLLLLLdVLLLL LjL%zL%zL%zLVLVL%LuLVL)FRR^R^^LL LLL LLLLLLLjL%zL%zL%zLVLVRR_ ^R^*^^^^^^^^ ^^^^QLL!bLbF^^QQLf-QQQL)ULLL-L-LLVR_B ^^s^oQQL)QUL-ULLLLAvLLVLLLVLLVL)LLVLLmLkLVR_ ^QLULLLLBvLLVLL-LVf"LLmL*L*LVLR^LLmLLR_a UL!f[UQLfJQLUQQLLL-LVLL!f^^L%f`^LLVR_ ^^^^QLULLL-LVLLVLL)LVLLR_QLLLLCvLLJ_*LL!b L%by^QL)ULLLLf LLVLLLVLLLLL LLLVL  L-L VL L)iR^tQL)ULLLLf LLVLLLLLVL LLLVL)L LLLVL aR^_QLUL)*L*LLLLVLLLLLLVL)LLV)LLL!b L%b ^BL-q^:QL L LL L L-LV LL LVL^R_QLUL)*LL LLL *L *L%zF LLLVLLVL*LLL!b L%b^ LLqV^ UL^F L LL)LLVUL L ULL *LVF L LLV L%LuLV LFL)iR_QLULL)L)L[_QLUL)*LL LLLLL L zL/c_QLLLL!b L%b^2LLV^&QLL*LjLVL!L)LV^*LLLDvL*LVL*L*L|LVLF_DQLLLL!b L%b^^QLLL)LV^*L*LmL*LLLLEvL*LV*L*fL*L*LuLVL)FLLL L L * L%zF R_*L*LmL*LLLLFvL*LV*L*fL*L*LuLVL)FLLLL L* L%zF R_QLULLL-LVLL)L[_QL!fLLVLtLV_^^QLLL)LVLVLj LLVl^ULLf LcaLw f3LGLLbLLVLL-L)L}V^ ^L/*LLLzLVLtLVLLVLLLHvL-LVL%LuLZR_QLj *LLVl^2LLf LcaLw f LLqV^ ^L/*L*LQb*LTLLVL!f4ILQL*LTLL-LL *zLVLLVJL*LVLjFLLLF_CUL%ffUUL!fVQLUQL)UQL-*LLLLL"LLLVLLLVLVR_^^^^QLUL)*LjLLLLLL LVLLvLVLLnVLLKL-LVL LLvLLVLMLLZV LLL!b L%b^$LLV^QLLL)LV^L LLLL LOvL)LVLL)PLLZVRl^LLf LcaLUDfLLLVLLVLLLLQvL-LVL)LLL!b L%b^9 LLV^-QL L L LLLjL%zLLV^L L L LLL LRvL)LVLL)SLLV L%LuLVR^ ^L/*Y^RrULQL)L-L9UL-L=LLLVL_VL=LVLL:R9LL-Lx"rL)LL)Rr9L=LzLV9LLV9LLLLL9LL=V9LtL=V9L)L[L=V9L)L=LYvLL*RLLjLLLLLLLLLLzL)LLLLLLL!L!L!L!LLjLjLjLLL!L!LUL zL-LfULLLxVLLLL9VLLL=VLQLjLVL!b LbL!fQL* LLLLLLF L)FL!LlLVL)L)LVvLQLTLVL!L-LLlL_VLLLWLLVXLLsVLLZvLLZV-L)LVL-LVRUL*L)* LVLxVQL)URr\7D7Ez7FS7G7HL/`S7_KLLL!b L%b^L-L`*^gL-L`*^9LLLLLL%zrrLLL%b L!b^*L^!^-L-L-L-LL9LL9LfVL%LjMr-L9LL9LLLfLLL%b L!b^QL)UL-*L*L-L9LL9f"-L-L-L-L L L L=VL=^qLLL%b L!bN^WQL)UL-*L*LLLLL=VL-L-L-LLLL=VL=^nL;Lc^^nL=LcR^_L)Lf)LLL%b L!b^QL)UL-*L*LL9LL9f" L L LL=VL-L-L-L=^r-LLL%b L!bN^WQL)UL-*L*LLLLL=VL-L-L-L L L L=VL=^nLFLc^^nLHLcR^^!LLLLLLfVL%Lj]RrLLL%b L!b9^?QL!fUL)*LL)R^$^^QLL9)^ dLb!^LLL%b L!bQ^WQL!f*LR^F^^QL)UL-*L*L-L9L-L-L-L=\^ qLe!^L)LL!f^;^^L!f)^*^^L9LLLL)L)LL=LTRriLiL_LiLaVL!LLL%b L!b^QL)UL-*L*L*L9LL=VLL!fL9LLLLju^@L!fLLLLLLt^)LLLLL-Lt^iL9LLiL%LjE^)LLLL-LLL9LwvaLL)RrLLL%b L!bR^XQL)UL-*L*L9L-L=VLL!f L^L!f L^)^ dLb^LLL-L-L-Lyva-LRrLLL%b L!bw^xQL)UL-*L*L9L-L=VLL!fL)LR^7L!fLLLLLd^LLLLLd^i^LLL-L-L9LL=L{va-LL)RrLLL%b L!bH^HQL)UL-*L*L9L-L=VLL!bL!f^LI^^LLL-L-L-L}va-L)RrLLL!b L%b^;^8QL)UL-*L*L-L9)L)L=VL9A^LLLLvaL!rLLL!b L%b ^Ui^QQL)UL-*L*L*LL9L)L9LLLL=L-LLj}R^LLLLvaLL)rLLL!b L%b ^>-^:QL)UL-*L*LLL9L)L=LL9^rLLLLvaL-L)r7]7^^#i7_^#K7`^#e7a^#&]7b^#F7c^#7d^#eK<7e^# S7fhLL!7i]Li?mLk7j]Lj?"63lLLmLLLovLLLL)LpeLLLrvaLLLsvLtLuLvLLxLzLLL|vL~LLLLL ?x L?DL ?XL ?L)? SL ?9VL-?ſL?8L?LL?\L?9лL??L?:L?fL ?ͱJL?!IR7IE7K7LL/`S7_ LA^^^^^^^^^^^^^____%_/_9_C_M_W_a_k_u_______________ ___)_3_=_G_Q_[_e_o_y_______________L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_~L)L"_rL)L"_fL)L"_ZL)L"_NL)L"_BL)L"_6L)L"_*L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_L)L"_vL)L"_jL)L"_^L)L"_RL)L"_FL)L"_:L)L"_.L)L"_"L)L"_L)L"_ L)L"^L)L"^L)L"^L)L"^L)L"^L)L"^L)L"^L)L"^L)L"^L)L"^L)L"^L)L"^zL)L"^nL)L"^bL)L"^VL)L"^JL)L"^>L)L"^2L)L"^&L)L"^L)L"^L)L"^9LLz9LLz9LLz9LLz9LLz9LL z9LL z9LL z9LL z9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LL8z9LL9z9L)L)L?zrrL^^^#^-^7^A^JUL)L"^>VL)L"^2WL)L"^&XL)L"^YL)L"^ZL)L"^9LL!z9L)L)L%zrr9LLz9LLz9L)L)Lzrr9LLzhLLLA^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^}^z^w^t^q^n^k^h^e^b^_^\^Y^V^S^P^M^J^G^D^A^>^;^8^5^2^/^,^)^&^#^ ^^^^^^^ ^^^L^ ^^^^^^^^^^ ^^^^__{_yQLL9Lj+=LLVLLfmLLnLLl^?LLf LaLw f=L)LLVL-LV^ ^L/*_^QLL9Lj+=LLVLLfmLLnLLl^?LLf LaLw f=L)LLVL-LV^ ^L/*^^QLL9Lj+=LLVLLfmLLnLLl^?LLf LaLw f=L)LLVL-LV^ ^L/*^^9L)L=LV=L=L)Lf^%rLLLL9L)L)LovL-LV!L-LL%L!LVLL)L=LpvLLVLLL-LVL)LzR9LLV9L!L9L!f|9Lf)=LLLLV9L9LL^LLLL!fLf^!L=LLL9LLLV9L!LL)RLLL9fDL=9Lf"LLL%LVLLLV^LL%L%LVLfL=LfLL%^]LLL!fL fLLLLV^,L-LLVL-LLVL-L LVL-RLLLLfL)LV^7Lf)LL)LLLVL)LLV^ rLLsLLVL-LLV!L!L!LLL)LL-LLtva-LLL LLLuvLLV)LQRrL^^2^b^^^^QL9L%LV9LLV9L!L*^QLUL9LLV9L=L-LVL)LL2^QL9LLV9LLLV9LL*^gQL9LLV9LLV9L!L*^BQLUL9LLV9L)L)L;^QL9LLV9LL*^9LLV9L!L9LLA^^^^^^^^^^^^____-_@_B_P_R_T_b_p_~______*_8_F_H_J_L_N_P_R_T_V_X_Z_\_^_`_b_d_f_h_j_l_n_p_r_t_v_x_________!_%___QL)L_QL)LL_QL)L_~QL)L_nQL)LL=_Y _UQL)LL _AQL)LL= _,QL)L _QL)L _ QL)L_QL)LL=__QL)L___QL)L_QL)L_QL)L_QL)LLL-LVLLLV_rQL)LLL-LVLLLV_IQL)LLL-LVLLLV_ QL)LLL-LVLLLV^^QL)L^QL)L^QL)L^^ ^!^"^#^$^%^&^'^(^)^*^+^,^-^.^/^0^{1^w2^s3^o4^k5^g6^c7^_QL)L8^OQL)L9^?:^;;^7<^3=^/>^+QLUL-L)L)L?^ L@!^LLL9fL)LL:^yL fL!bL%fL)LL)LL%L:^HL!fLfL)LLLVLL:^L)LLLVLL:RrLL-LwLVL9LLL)QLLLLVL-LLVLLVL)L=LxvLLVLyL-LVLL-LLzvLLRRRr9LLL!f =L^=LLVLLLLL-L-L-L|vaL!R9L=L9f_Q=LLL%L!fJf=LLL%LL쨥^L%LLLf LL)L!^LL!f`LLLLL!LL)f'LL)LLLVLL%_LLL)L1R^LL!f3LL)LL)LLLVL%L^O=LL=LLL-LL-LL)L LLLLV-L%L)RLLLLf^L)LL)LLL쨥LL!f ~LLL9LvLVLLL!L!L)L!L!LVLLLL LLLLLLLva!LLR 9LL^^^^:^T^b^|^^9L=L\^^x9LLLLLL^V^c^Z9LL9LLVL`^G^>9L=Lb^7^.9LLLLLLdVR^^9LLf^ ^jLL!f^r9L=LLLj LLVLLf jLl^ALLf LaLw fL)LLV-L%L^ ^L/*)R9LLVLLf jLL-rLL9L)L9LLVL^6^4^2^0^.^,^*^(^&^$^"^ ^^^^^^^^^^ ^ ^ ^)^F^c^^~QLLL)L=VL!^m^_QLLL)L=VL#^N^@QLLL)L=VL%^/^!QLLL)L=VL'^^LSLVL3RLL%f1QLUL9LL!LL=VL-L^VLVR^ ^^rkLLVLLwf jLLLLLLL)L!b)LbL!bLbL!bLf jL)L L9L=LvLVLLLLL L9LLLLvaL-L%LLVL!LLLL)LgLLLL^ ^^-^T^f^dL)L^a^ULL%L)L^K^?LfLL^LL)L^"^LL)L^^LLVLLLLA^^^^^^^^^^__ _P_X_`_h__________#_I_o_s_{________________________________ ___&_*_(_+_"_%_____L__LL__L__L__jLLVl^/LLf LaLw f jL^ ^L/*L__ __LL __jLLVl^/LLf LaLw f jL^ ^L/*L _O_FL_E_<L_;_2L_1_(jLLVl^/LLf LaLw f jL^ ^L/*L____L______L__L__L__LLLLLL%zL!__LLLLLL%zL#_b_YLLLLLL%zL%_:_1LLLLLL%zL'__ )_ _L*_^L,^^L.^^0^^1^^2^^3^^4^^5^^6^^7^^8^^9^^:^^;^^<^^=^^>^^?^^@^^A^^yB^|^sC^v^mD^p^gE^j^aF^d^[G^^^UH^X^OLI^N^ELK^D^;M^>^5N^8^/O^2^)P^,^#Q^&^LL)LLRV^^T^ ^jLLLLLLVLL)Lf^%LLVR_s)Lf jL-L-L)LLV-LLLLvL-LVLLvLLVLR l^KLLf LaLLb Lt! b^jL^^jL^ ^L/*9LL)L^^^\^p^^^QL,LL^QLULL!fL=fL)LLVL)LLLL^QLLLL^lQLLL^ZQLULLLLLLLLVLL)LLLLVR^QLLL^LL+r9LL)LLfE^L-LL LVL-L=LzL#LELLr9L)LVf =L LV=LL-L-L:^t^r^p^n^p^^^^^b^^^^__$^T_1^P^N_@_S_f_y___^>___^6^4^2^0^.^,^*^(^&^$^"^ ^^^^^^^^^^ ^ ^^____QLLLV__QLLLV__QLLLV__QLLLV__QLL숥__QLLLV__QLL숥__wQLLLV_m_bQLLLV_X_MQLLLV_C_8QLL숥_2_'QLLLV__QLLLV_^QLLLV^^QLLLV^ޥ^QLLL)LV^ĥ^QLLL)LV^^QLLL)LV^^QLLL)LV^v^kQLLLV^a^VQLLLV^L^AQLLLV^7^,QLLLV^"^QLLLV^ ^LLzLVLL)Lf^%rLLL9LLL)QLLLLLVLLLLVLL)LLLVLLLVLLVLL)L)L=LvLLVLLLVLLvLLVLLLVL!L)L L-L-LLvLLVLLLsRRr777z77E7 77#9V7#17#K7#ſ7#f7#9л7#9V7#7#F7#p>7#97#Q 7#!n7#VK7#tK7#q7#ſ7#7#f7#eK<7#N7#a17#W27#;7#!I7#ve7#07#W7#7#!I7#lY7#S7#!|7#7.7#(07#L(7#C7#7#&]7#MR7#(07#)7#f7LL!7L?HL%7L?!L7L?Dm>L7L?L7L?Nƅ?L7L?gv)L7L?d]L7L?L?L7L?٣L 7 L ?|L 7 L ?12L 7 L ?WL7L?fL7L?,)L7L?#L7L?عyL7L?u L7L?0%L7L?oL7L?=:5L7L?q=L7L?D,L 7L?%9L"7!L!?B1L$7#L#?L-7,L,?j)L/7.L.?L70L0?J+L 71L1? L!72L2?P L"73L3?1L#74L4?`a?L$75L5?>3L%76L6?13L&77L7?:L'78L8?V?L(79L9?V?L)7:L:?xn8L*7;L;?CEL+7<L<?1L,7=L=?{'CL-7>L>?<L.7?L??:;L/7@L@?M>L07ALA?xE6L17BLB?BL27CLC?:L37DLD?;L47ELE?N L57FLF?@L67GLG?[/L77HLH?@;LJ7ILI? 8LL7KLK?Nh7L:7MLM?#L;7NLN?#L<7OLO?@" L=7PLP?@" L>7QLQ?\@LS7RLR?n?L@7TLT?Ґ2[LL]7\L\?$L_7^L^?ǀ La7`L`?Ɛ7Lc7bLb??Le7dLd?ALg7fLf?oiLLg(7jLj?$<LLkLlLLLqvLvLLLLL{vL}LLLLLLLLvLLLLLvLL?y/^L?lYL ?/ L?ڥAL-?pd{L)?VKL?77L?p!>L?IڕL?4}rL?UR 7M7NM#777OL#*=7PM#0%7QM#/ 7RM#?7SF#cV7TM#:7UM#J+7VM#Nh77WM#j)7XK#7YH#ſ7ZM#,)7[M#:7\M#:;7]M#@" 7^H#W7_M#@" 7`M#CE7aE#7bN#7.7cL#5`*7dN#iW7eM#;7fM#Ɛ77gM#L?7hM#o7iN#MR7jM#%97kM#B17lF#7mH#9V7nN#K7oM#Nƅ?7pN#F7qK#W7rH#^'X7sM#@;7tM#q=7uM# 87vL#в7wH#!I7xM#\@7yM#d]7zM#137{M#A>7|E#!I7}M#{'C7~N#>#-7M#o7M#N 7H#7M#عy7M#B7H#7M#<7M#7I#ſ7E#f7M#H)Z7F#ſ7M##7I#9л7I#!I7M#[/7H# S7M#Hwy7L#N+7M#gv)7E#?7M#V?7N#!n7N#i7L#7M#A7N#e7M#u 7L#^7L##o7M#<7L#47M#!7N#7M#xE67L#77M#`a?7N#!|7M#n?7N#L(7F#^'X7N#&]7L#K97M#>37I#:7M#٣7M#127E#ſ7M#V?7E#9л7N#>37H#f7M#xn87M#P 7N#M7M#Dm>7M#H7M#=:57M#f7L#ſ7M#7M#|7M#@7M#W7M#D,7N#O177M# 7L#~277G#17M#17M#$7M##7M#17M##7M#Ґ27M#ǀ 7N#(07M# 77F# S7M#M>7LL7DL?u_:L7DL?ЗL7DL?w'L7DL?B(L7DL?˩L7DL?!$L7DL?>LL7DL?;WLLL)LLLL-LL)LLvL-L)LvLL-LvLLLvLLLLL LLLLLLLLLva LLvLLLLLLLLLLL)LLLv2LL2LL 2LLL"v2-LL)LLLL L L#v-2)LLLLL L'v)2LLLLLL LLL=veLLLLLLLLLLLLLLLLLL+LLTvaLL LL[vL\L?$\L?lY\L?\L?<*\L?7\L ?\L?z\L? \L ?-8\L?]U\L ?H>\L?MP\L?i\L?c1'\L ?)\L? \L?)\L?`\L?̫(\L-?\L?7\L?\L?\L?0f\L?s%o\L?\L?Q p-\L ?`$R\\6 S6 E6 L/`S7_\LL9LL3*zLL"rjLLVLLLVLLL!bL%b^>^:L#LV^^*UL!fQLLL R^d^^^LLLLL!b L%b^5L'LV^)QL)ULLLL)LLV^LLLL QL ULzRl^*LLf LaLw f)^ ^L/*!rL9LLL%fUUL%fGUUL!f7QLUQL=L=L-LVL=L-LVLR^^^ ^^^^LjLLl^LLf La^L/*LLLL!b L%b ^^QL9LL=VL^L)LLVLLL+rL)LL+rL9L)L+r9LLL=VLLLVLLLLbL b^^)LLL%fLUL%f>UUL!f.QLUQL9LL=VL9L-L=VR^^^ ^^^^LLLL)LL%z^i^9LLVL!fLL9LLL=VL^2^LLVL!fLL9L-L=VLL)L)L9VL)LL=LLvL-LLLLbLbLbLcLcLcLcLc+LcLcLcLcL cLcL2cL cL cL ckL:cLcLcLcLcLc__LLVLLLLL__LLVLLLL__hLLVLLLLL_W_@LLVLL!LVL$f(LL%L-LL%LVLL^LLLbLb(Lb3Lb>^N^JLL^M^8LL^;^&LL^)^LL^^L)LL__u-LL=LLLVL_g_P-LLLL=VL_G_0LLVLLLLL=VL)LV__LLLL!b L%b ^?L^7QL)ULLL-L=VLL=L-LVLV^__L-__-LL=LvLLLVL__iL-_s_\L-_f_OLLL%fUL%fUULL!bL%bA^^QL)UQLLL-L=VLL-L=VLLVR^~^hUUUL!fTQL)UQL-UUQLLLL=VLLL=VL LL=VLLVR^ ^^^^^^^L_z_cLLVLLL_\_ELLVLLL_>_'LLVLLVLLLLLL=VLV__j LLl^LLf La^L/*LLLLL!b L%b ^^QLLL=VL^L__zj LLl^LLf La^L/*LLLLL!b L%b ^^QLLL=VL^L__-L_^L-_^-LL=LvLLLVL^^LLVLLL^^LLLL=LLLLLvLLLVLLL!b L%b ^*L^"QLLLLLLV^R^?^(LLLVLLLL)R^^LLLLARrLLLLL%f3QL!f%UL!fQL-LL9V^^^ ^^^^ L)rLLLL)LLL%zL-LLL!b L%b ^$^ QLL9L)L=VLL%z^L3RLLLL)LLL%zLL9LL=VLL%zL3RLLLLL9LL=VLL9LL=VLL%zL%zL3RLLLsLLLL^)_;_]____<_`_____r____W_{__-QLL ^^-^E^]^u^^^^^LL)LL^LL)LL^LL)LL^LL)LL^QLLLLL^jQLLLL^PQLLLL^6LL)LL^LL)LL^_QLL)LL9L)LV_QLL)LLL-L9VLL%z_QLULLLL-LLLL9VLL%z_QLULL-LLLL9VLL9LLVL%z_RQLULL-LLLL9VLLLL9VLL%zL%z_QLL)LL9L vL)LV_*LL!b L%bJ^QL)ULLLLLL9VLL LL9VLL%zL%z^GQL)ULLLLL-L9VLL LL9VLL%zL%z^_DQLUL)*LL LLLL9VLL LL9VLLLL!b L%b ^$^ QLLL)L9VLL%z^L%zL%z_QLUL)*LL2L)LLLLL9VLL LL9VLL%zL%z_gQLULL LL-LVL-LLL-L9VLL%z_#QLUL)*LLL-LLLLL9VLL LL9VLL%zL%z_QLL )LLLL!b L%b ^$^ QLLL)L9VLL%z^_|QLL )LLLL!b L%b ^$^ QLLL)L9VLL%z^_,-L:_#QLULL-LLLL9VLLLL9VLL%zL%z^QLL)LL9L vL)LV^QLL-LL^QLUL)*LLL9L vL)LVLLLLL9VLLLL!b L%b ^:^6QLL LLLLL9VLL%zLVLL%z^L%z^-Lf LLLLLL%z^(-LfLLLLL%z^L)LLL!b L%b ^^QLLLLL%z^LL)L)LVLLkRrLL97z7S7E77L/`S7Y_L^ ^^^)^2gL)L"^&(L)L"^)L)L"^*L)L"^9L-L-L-L!zrr9LL%z9LLz9LLzLL9LLT5z L #xBLL)L0?xBL L #xBLL)L.?xBLL/mLL/1&LL!LL-f.-L$L)LL)L/AVL)L/2L%e_ L)L #xBLL #Hg Lz?Hg  L?xBLRr #Hg L #xBLL L)QL-LLL# sLLL# sL,V?xBL L)U?Hg R8LL?Hg L?xBLL9? L:?- L;?m[L<?m7Lj#LL>V#xBLLL# sL2lrl^LLf L5L/*L/*!LL!f^^^LL%f^^^LLf^^^L^ ^^!^(^6*L9LL*^%DL%^DL%^QL9LL*^L^ ^^!^(^6*L9LL*^%FL%^FL%^QL9LL*^LL!fQL^^^HL%LLLbL%b^0^,QL9LL#VR^$^QL9LL#VR^^JL%LL!f+*LL$LLKL)LVL^^^JL%LLL9LL)LL!f!UL)LMLLVL!^^^NL%)rLLELLQL)L"LRL^ ^^^^QLUL)*LLPLsLRLL'VLVLL&VLL!fLSLV^0^^LL$LVL9LLVLTLL&[^OQL-LL2^>QL-LULV-LLV-LVL2^QL-L9LL2^rL LLLLL9VL)R788L? 8L?xBL8L?m8L?- 8L?Hg 8L?m[Y7z77E7 77#K7#ſ7#9л7#lY7# 7#57#Z7#17#(07#q7 #iW7!#C7"#!I7##f7$#eK<7%#a17&# S7'+LL-7,L,?"63L/7.L.?==L170L0? `L372L2?;4LL675L5?;WL=77?LL/HoL#g%Z7>@LALBLCL)LEL-LGLILLLOLLLLWaLXLYL)?lYYL?UJYL ?3U=YL?1YL?JWYL? YL?+0YL?YsYL?2YL?RYL-?JYL ?~R YY77#ſ7#n7# 7#*=7#N+7#n77#cV7#7#V/7#7#e7#J7#r!7#7#^7##o7#,7#47#77#|57#"637#37# P7#!|7#7.7#5`*7#K97#&]7#MR7#JW7#7# 7#K7#-7#>37#F7#W7#R7#}7#!;7#L;7#7#N7# 7#7#eK<7#oc|7#17#в7#7#ط7#O177#\7#3U=7#|7#~277#?7#17#:z7#(07# S7LL7L?;WLLLL)L)LLvaL LLLLaLLL)?L? L?L?f0 L?HL-?3U=R6L/`S7_LLLLL!fLL%LVL!f9L)L!L-L%LpVL*^9L)LV9L LVL=!RLLLL)L)LvaLL!RLzL)LmLLLzL+rLL!fTQLj9LLlVL=L L)L}Vl^)LLf LaLw f^ ^L/*^ ^^rLL!f"QL9LL=LLkVLuV^ ^^rLL9LLLfL!LLpVLfOLL)LLLpVLLLVLLLLr)LLVLL{LLrL-R^ZL|LLLLLL-L%L!LVLiL=LLVLLzLLLLLwVLLRLLLL|LjLxL)LsLL)LvLLyVjLLtLoLol^jLLf LaLL~LLLnVLLL9e*fQLLo^^^)LL^ L/*L)LvLLyJRLL9L)L)LvLfj Ll^LLf LaLL_:bL (I/b,LZg6bE^bQL)ULLvLL=VR^P^BQL)ULLqLL=VR^0^"QL)ULLLL=VR^^L/*_\R7Z7[7\\7]S7^L/`S7_#L^ ^^^&L)L"^L)L"^L)L"^9LL!z9LL%z9LLzLL9L)L)L (I/zrrL^ ^^$^7QLLL^%QLL숥^QLLLL^LLVLrLLL(bL-bL.bL0bL/bL1bL2bL3bL4bL5bL6bL7bL+bL,bL)bL*bL"bL#bL$bL&bL'bL%bLbLbLcL|cLcL cL!cLcLcLcLc__ ___^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^z^x^r^p^j^h^b^`^Z!^X^R!^P^J%^H^B%^@^:^8^2^0^*^(^"^ ^^^^^ ^^L)rLLLLL fiQLUL)*L)L9LL9f>LL-L=VL-LL-LVLLL-LLVLVR^9^R^ ^^LLLVLLLLLVLJRrLLLj L9l^$LLf L-Lf^S^L/*Lj L=l^%LLf )LLfR^^L/*LL)L)L%zR^L!LVL%fSL!LVL)L%LVj L=l^%LLf )LLfR^L^L/*LR^E^^L!LVL!f!L!LVL)L%LVR^^^L/&]!9LLLL!LVL fL!LVQLf~L!LVL)L%LVj 9L=l^%LLf )LLfR^^L/*LLLLVLLL-LLVL9LVR^z^^^^L!LVL%f&L!LVL)L%LVLR^=^^LLLVLLLLVL9LV^ L/&]!LLLL!LVL fL!LVQLfL!LVQQL)L!LVL-L!LVQLL%LVLLLL!LVL fLL!LVQLf6L!LVL)L%LVLLL)LVL9VR^.^^^^-L-LLL=V^ L/&]R_ ^^^^L!LVL fCL!LVQL)L!LVL-L%LVL)LLL=VR_ ^^L!LVLfL!LVL)L%LVj Ll^%LLf )LLfR_@ ^L/*LLLLL!LVLf6L!LVL)L%LVLLVLLL=VR^P^^L!LVL!f.L!LVL)L%LV$LLLVR^^^L/&]R_ ^^L!LVLfL!LVL)L%LVj Ll^%LLf )LLfR_B ^L/*LLLLL!LVLf;L!LVL)L%LVLLVLLLL=VR^P^^L!LVL!f.L!LVL)L%LVLLLVR^^^L/&]R_ ^^L!LVL fL!LVQL!f{L!LVL)L%LVjLLVl^%LLf )LLfR_) ^L/*LLL-LLVL)LLL=VR_ ^^^^L!LVL fL!LVQL%fL!LVL)L%LVj Ll^%LLf )LLfR_ ^L/*Lj Ll^%LLf -LLfR_X ^L/*L)LLLVL)L)LLVLL=VR_+ ^^^^L!LVL g L!LVQLfL!LVL)L%LVj Ll^%LLf )LLfR_^L/*L-L!LVL f-L!LVQL%fy-L!LVLL%LVj  Ll^%LLf LLfR_H^L/*L-LLLVLLLLVLL=VR_^R^^R^^^^^L!LVL g(L!LVQL gL!LVL)L%LVj Ll^%LLf )LLfR_^L/*L-L!LVLf-L!LVLL%LVj  Ll^%LLf LLfR_?^L/*LLLL!LVLfBL!LVLL%LVLLVLLLLVLL=VR_^R^^R^^^^^L!LVL fL!LVQLfL!LVL)L%LVj Ll^%LLf )LLfR__^L/*Lj Ll^%LLf -LLfR_.^L/*LLLLLLLL=LLvaL쭥R_^^^^L!LVL gjL!LVQLgSL!LVL)L%LVLg.)L%LVL-LLVj Ll^%LLf -LLfR_h^L/*LLLLL!LVLfvL!LVL)L%LVj  Ll^%LLf )LLfR^|^L/*LLLLVLL)LVLL=VR^R^^L!LVL!f.L!LVL)L%LVLLLVR^^^L/&]R_^^ ^^^^L!LVL fL!LVQLfL!LVL)L%LV-LLLj  Ll^$LLf L-Lf^8^L/*L-LLLVLLLLL=VR^RL!LVL%f4L!LVL)L%LV-LLLL=VR^^^L/&]R_^^^^L!LVL fL!LVQLfL!LVL)L%LV-LLLj  Ll^$LLf L-Lf^8^L/*L-LLLVLLLLL=VR^RL!LVL%f4L!LVL)L%LV-LLLL=VR^^^L/&]R_^^^^L!LVL fEL!LVQLf.L!LVL)L%LVLLL=VR_/^^^^L!LVL g_L!LVQL gHL!LVL)L%LVj Ll^%LLf )LLfR_^L/*L-L!LVL f-L!LVQL f-L!LVLL%LVL fL%LVQLfL%LVQQLL%LVLLLVj  Ll^%LLf LLfR_^L/*LLLLVLLL-LVLL=VR^ᥥ^R^"^R^^R^^R^^^^^L!LVL fL!LVQLfL!LVL)L%LVj Ll^%LLf )LLfR^N^L/*LLLLVLL-L!LLL-LVLR^^^^^L/&]!LLLL!LVLfL!LVL)L%LVL fp)L%LVQLfV)L%LVQQL-L%LVLLLVLLLVLL-LVLL9VR_^^^^^^L!LVLfL!LVL)L%LVj L=l^%LLf )LLfR_P^L/*LLLLL!LVLfCL!LVL)L%LVLLLVLLLVLL9VR^P^^L!LVL!f.L!LVL)L%LVLLLVR^^^L/&]R_^^L!LVL fL!LVL)L%LVj Ll^%LLf )LLfR_F^L/*LLLLL!LVL fCL!LVL)L%LVLLLVLLLVLL9VR^P^^L!LVL!f.L!LVL)L%LVLLLVR^^^L/&]R^^^L!LVL fnL!LVQL)L!LVL-L%LVj Ll^%LLf -LLfR^.^L/*L)LL)LVR^^^-^ L/&])rLLLL!LVL g2L!LVQLgL!LVQQL)L!LVL-L%LVLLLL!LVLfL!LVL)L%LVj  L9l^%LLf )LLfR_^L/*Lj  L=l^%LLf -LLfR_[^L/*LL)LL)L%zLR_>^^L!LVL fL!LVQLfvL!LVL)L%LVj  Ll^%LLf )LLfR^^L/*LLL-LVLLL)L%zLR^^^^^jLLLL LVl^$LLf L-Lf^X^L/*Lj  Ll^%LLf )LLfR^'^L/*LL)L)L%zLR^L/&]R^S^^^^j Ll^$LLf L-Lf^^L/*LLR^ L/&]!LLLj L9l^$LLf L-Lf^S^L/*Lj L=l^%LLf )LLfR^^L/*LL)L)L%zR^yL!LVL%fSL!LVL)L%LVj L=l^%LLf )LLfR^ ^L/*LR^^^^ L/&]!LLLL!LVL fL!LVQLfL!LVQQL)L!LVL-L%LVLf-L%LVLLLVj  L9l^%LLf LLfR^^L/*Lj  L=l^%LLf LLfR^^L/*LL)LL)L%zR^^R^^^^^L!LVLfSL!LVL)L%LVj L=l^%LLf )LLfR^ ^L/*LR^^^^ L/&]!LLLj L9l^$LLf L-Lf^^L/*L)L!LVLf)L!LVL-L%LVj L9l^%LLf -LLfR_^L/*Lj  L=l^%LLf LLfR_d^L/*LLLLLLLL%zR_B^^L!LVL g L!LVQL fL!LVL)L%LVLf)L%LVL-LLVj L9l^%LLf -LLfR^^L/*Lj  L=l^%LLf LLfR^z^L/*LLLLLL!b L%b^&LL^LLLLV^R^0^^ ^^^^L^ L/&]!LLLL!LVL fL!LVQLfqL!LVQQL)L!LVL-L%LVj L9l^%LLf -LLfR^^L/*LL-L)L%zR^^^^^L!LVLfSL!LVL)L%LVj L9l^%LLf )LLfR^ ^L/*LR^^^^ L/&]!LLLj L9l^$LLf L-Lf^S^L/*Lj L=l^%LLf )LLfR^(^L/*LL)L)L%zR^^ L/&]!LLLL!LVLfRL!LVL)L%LVj L9l^%LLf )LLfR^ ^L/*LR^^^^ L/&]!LLLL!LVL gL!LVQLgwL!LVQQL)L!LVL-L%LVLLLL!LVL fL!LVQL(fL!LVL)L%LVj  L9l^%LLf )LLfR^^L/*LjLL L=Vl^%LLf -LLfR^^L/*LLLLLLL LL%zR^v^^^^jL L=Vl^$LLf L-Lf^3^L/*LLLLLLLL%zR^L/&]R^^^^^L/&])rLLLL!LVLfUL!LVL)L%LVjLL9Vl^%LLf )LLfR^%^L/*LR^^^-L^ L/&])r9LLVLLLLLbLb^,^(QLL=R^#^QLL=R^^L)쥥LLLL-L)LLvajLL9LLLl^CLLf LaL65f!)LLL-LL=V^ ^L/*)RLLLLLL-L-LLQLVL9)Rr77C7S777L/`S7_KL^^^#^-^7^A^JQL)L3"^>RL)L3"^2SL)L3"^&TL)L3"^UL)L3"^VL)L3"^9LL!z9LLzLL39L)L)L_:zrrL^^T^V^X^Z^i^jQLLFL f LFLfcLL*V^A^^QLdLLFL**^#e^f^g^QLhLL**^i^L)LOLaVL.r9LL2L)LC#LOLrLLL)L"L@L9LLL#L@L9LLLj9LL4VLl^4LLf L+aLw f)L=L@^ ^L/*L="L9LNL9LNL9LNxLL39LLI"W3zLL9L5L9LEL9LL9L$L9LL9L%L9LL9LL9L6L9L9LNLLL%L)LL%L7VL)LLL@L9*RLL@L9LAL@L9L&L@L9LBL@L9L9LOLLILL'j L9l^7LLf L+aLUDfZL-LaVL.^ ^L/*)LOL)LLVL)LKLL@RLOLLILL'j L9l^7LLf L+aLUDf[L-LaVL.^ ^L/*)LOL)LLVL)LKLGRLLLLLL%L;VL f^L-L-L!LLL7VLML92RLLL1L9LLL1L9LL1L9L4L1L9LLLL!b L%b^$)LJL9*^QL-LLXL=2^LILLL<VL9LILLL<VL9HL.LIL"L>VL9LIL\L>VL9LIL L>VL9LIL L>VL9LIL L>VL9LL%LL7VL)LLfLL]L9VLILL L>VL=L_L9LILLL<VL9HL.LwL.LILVLwL.LILLL<VL9HL.77z7S7E7 7  7 7 #ſ7  #7 #nN 7 #7 #b 7 #8g7 #4w7 #7 #*=7#7 #n77#^7 #7 #=7 #z 7 #7#97 #7 #)7 #-zK7  #!@7! #77" #|57# #e97$ #"P=7% #37& #I7' #!|7( # P7) #L(7* #7.7+ # 7,#.:7- #&]7. #;7/ #MR70 #-71 #':72 #K73#9л74 #i375 #876 #W77 #?78 #-79 #N7: #N7; #!I7< #в7= #07> #7? #Ci7@ #|7A #~277B#!I7C#:7D #7E #T7F #?7G #>#-7H#*Vc7I #47J #(07K #ǖA7L #q&7M#+7N#H>7O#f7PWLLY7XLX?6L%7ZLZ?T'qL7[L[?UL7\L\?L^7]L]?E@37#nN 7#7#-7#f0 7#7#N+7#M7#V/7#}7#!;7#L;7#7#7#!7#7#ڥA7#"7>7#^7# 7#f7# >7##o7#>97#oc|7#]U7#7#в7#O177#,7#47#\7#f7#77#?7#17#f7#!|7#47#5`*7#7.7#iW7#&]7#K97#57#(07#MR7#:z7#+7LL7L?;L?f0 L)?3U=L?yL?~L?r1]L?9uL?^_L ?W"L ?} L ?5L? L?)L ?D.'L?Mu4R7_7`E7aL/`S7 _ L ^^^)^3^=^G^Q^[^e^nL)L"^bL)L"^VL)L"^JL)L"^>L)L"^2L)L"^&L)L"^L)L"^L)L"^9LL%z9LLz9LLz9LLz9LLz9LLz9LLz9LLzLLLL!b?L%bLb@LbBLbDLbFLbHLbJ^N^J)L!^A)L!^8)L!^/)L!^&)L!^)L!^)L!^ )L!^L ^^^"^.^:^F^R^^^j^u^rQLL!^dQLL!^VQLL!^HQLL!^:QLL!^,QLL!^QLL!^QLL!^L/1&L$L9rL9L)LL)@rL/HoL/Ho#QLL#.Z!L-QL2L!fL%aLL)$e_LLLCRrLLL9LL9LL9LLLVL9rL9L-L-L)L;rL 7z77#K7#-q7#eK<7# S7LL!7L?]8L7L?99L7L?f=uL7L?j0L7L?ǁ=L7L? L7L?XIL7L?L7L?cmLL/HoL#g%Z7L%L/HoL#g%Z7L%L/HoL#g%Z7L%L/HoL#g%Z7L%L/HoL#g%Z7L%L/HoL#g%Z7LL/HoL#g%Z7LL/HoL#g%Z7L%L/HoL#g%Z7LL/HoL#g%Z7LLL)LL)LLLLLLLLLLL LLL LL LL L L?q1 L? L)?/ L?۝ L ? *5 L?2 L?R L?o>- L-?a L?s L?b$ L?g| L?V L ?1& L ?Mu L ?2l L ?NkR  7bL/`S7 _HL^ ^^^& L)L "^ L)L "^ L)L "^9LL%z9L)L)LzrrL/L!LL/1&LLfC)L)$L)LL)f ^ )Lf )L ^)L-L V@)L%2_)L L#.Z!rR/QL /V(L L^ ^^&^C9L L "^7QL9L L)L 3^"QLUL9L L)LL ;^L LL "r LL9L= 7 7 7 7  #K7  #˿7  #ſ7  #lY7  #a17  LL!7  L ?{2#L 7  L ?̨gL 7  L ?t= 7  L L L)LL vL L?-X L-? M L)?4}r L?R  7d7e 7f7g7h_#3U=7i[# >7jb#a7k\#9л7l^#7md#4}r7nh#7oa#W7p_# 7q[#LL7rh#M7sb#7t\#4H(+7ue# 7vg#lY7wb# *57x`#Q 7y[#q7z[#x37{[#ڥA7|b#/7}d# M7~a#7a#N7f#!I7f#f7[#&7h#-X7^# >7]#s%o7[#a17f#07^#f7^#۰127[#Edž47b#b$7f#(07h#7.7[#˿7]# 7[#~87g#VK7[#H 7\#f7LLLLvLL?KVL)?<L-?VKR66L/`S7l _m LLL!b L%b^WW L9L2 LL !^EQL)ULjL9LLF Vl^$LLf LJ aL=^L/*9^LL)L)LX vaH LsL9L%zLL^^&^d^^_"_[9LM L9L-L@ V_BQLUL9LM L9LL@ V=LH L-L-LzL=L%z襥_j9LL: Vl^?LLf LJ aLw f9LM L9LL@ V^ ^L/*!^j9LL: Vl^?LLf LJ aLw f9LM L9LL@ V^ ^L/*!^dQLUL9LFLL-L= ^>QL9*L+ L6 VL!f9LL= F9LLI L@ VL^9ULM L9UL)L@ VLLL9L)LE VR9LL@ V9QL=LL; VL@ VLL%L* f#9QL=LL; VL@ VLL%9LM L!LT L=LH L)LLLzLL%zrLLLLL!b L%b_` LL/ :_QLgUL%gvUQLgfUUL%gVUUQL gDUUQQLa g1UUUL%gUUUQLg UUUUL%gUUUUQL gUUUUQQLa gUUUUUL%gUUUUUQLgUUUUUUL%gUUUUUUQLgjUUUUUUQQLb gOUUUUUUUL%g5UUUUUUUQLgUUUUUUUQQLfQQL)UUUUUUUUL9L)L; VLLfQLj=*LL- Vl^8LLf LJ aLw f=L)LVL= ^ ^L/*LLLL!b L%b^Fc LLALL2 LL ^,QLLA LLL L L^^^^d L2 LL R_^^J^^F^^B^^>^^:^^6^^2^^.^^*^^&^^"^^^^^^^^^^^^ ^^^^QL)ULLL=LM LB VL^6^4^2^0^.^,^.^(^&^$^"^C^^\^^^^^^^^ ^ ^g^~^^^^QLLL; VLA L^^QLe fLA L^^^^QLLL; VL< L^q^fQLT LL)L V^X^MQLP LL)L V^?^4QL' LL)L V^&^QLU LL)L V^ ^)LLL%^RULQL)L-L9L-LL; VL9LL; VL=L-L)LLB KRQLL)L9L-L=L-L; VLLQ VLB ;RLL9 LjL9LLS LLL-L0 + LO L!LL)LL-LZ vLL, VL*LLL!b L%b^- LL= F^QLLf[ L2 LL ^LM LULM L UL L@ V L LG L@ V L@ LH L4 LH L7 LH L,L> LH L%L? LH L L< LO L%zL%zL%zL%zL%zL) VL!L)L& VL QLN V *L L+ LD VLM LLL& VLLLL!b L%b^H-LL!LL& V^3QL)QLL\ L)L, VLL] L)L, V^L!LO LL-L-L^ vLLL-L_ vLLLLL=LLLL LLL Lf v a L( L!LLK LO L6 VL!fLL-_LLg vLL) VLLh vL L) V*LL L= L1 VL LA L@ VQL!L L@ VQL!L L@ VRr9LL=VLC L. LL. LL. LLV L+ LLL. zLL9Lj vL)L) V*fLUL)QL5 VL@ V)LLR VLL)L-L3 VL8 )Rrl 7 7 7! z7" 7# 7$ 7% # #9V7& $ #H)Z7' # #^7( " #ſ7) $ #777* % #>37+ # # S7,  #9л7- # #f7. % #F7/ ! #LL70  #4H(+71 % #;W72 $ #lY73 $ #H74 $ #A75 % #e76 $ #=:577 ! #G978 % #-X79 # #>7: # #N7; $ #,)7< % #O177= $ #W7> $ #D,7? # #!I7@ $ #d]7A # #W7B % #7C  #!I7D % #S7E ! #Edž47F $ #$7G % #!|7H $ #o7I % #7.7J % #iW7K % #&]7L # #7M # #7N % #MR7O $ #<7P $ #ǀ 7Q ! #ua7R $ #VK7S $ #B17T $ # 77U  #f7V /Ho#QL" L#.Z!LLY LLL)L)Li vaLk Ll L? l L?l L-?Ol L)?nRl l 6L/`S7Q _L ^^#^-^7^A^K^U^_^i^s^}^ L)Lu "^z L)Lu "^n L)Lu "^b L)Lu "^V L)Lu "^J L)Lu "^> L)Lu "^2 L)Lu "^& L)Lu "^ L)Lu "^ L)Lu "^9LL!z9LL%z9LLz9L)L)Lzrr9LLz9LLz9LLz9LLz9L)L)Lzrr9L)L)L zrr9LL zLLL!b L%b^uL-Lu *^*L-Lu *^9L-L-L-L!zrr9LL%zL^)^3^=^G^Q^[^e^o^y^^^^^^^^^^^L)Lu "^ L)Lu "^ L)Lu "^L)Lu "^ L)Lu "^L)Lu "^ L)Lu "^ L)Lu "^ L)Lu "^z L)Lu "^n L)Lu "^bL)Lu "^V L)Lu "^JL)Lu "^> L)Lu "^2 L)Lu "^&]L)Lu "^ L)Lu "^`L)Lu "^9LLz9LLz9LLzL^ ^^!^+^5^>L)Lu "^2 L)Lu "^& L)Lu "^ L)Lu "^ L)Lu "^9LL!z9LL%z9LLzLLu 9L)L)Lr)zrrLL VL rL^)^+^-^/^1^3^5^7^9^;^=^?^A^C^E^G^I^U^i^o^k ^g ^c^_^[ ^W^S^O^K$^G^C+^?^;^7 ^3 ^/QLL !^!QL LL L ^ QL^L^ ^^-^8^:^;QLLL9^)QL LL ^QL L숥^  ^ ^L LrL LL LL L9L9L L9L L L9L LL L9L L9L L9L L9L L9L L9L L9L L9L L9L L9L L9L L9L L9L L9L LL LL jL9L Vl^2LLf L aLUDf L-L=V^ ^L/*)L LjL L l^'LLf L a LL=V^L/*L-L)L{ VLL RL9L LL L L L9LL L L9L} LLL!b L%b^))L L9*^QLL LL L=2^L LQLULL)L9VL; L VLL=:_QL-LL9V-LL 2^QL-LL9V-LL 2^QL-LL VLLL9L-L= vaLLL :^QL-LL VLLL=L-L> vaLLL :^aQLULL? L-L VLL9:^37x t #-7y q #n7z p #7{ q # 7| p #^7} t #M7~ t #7 t #!7 p #97 s #!I7 s #f7 t #>97 n #a17 t #O177 r #3U=7 s #07 r #UJ7 p #:7 n #lY7 p #17 q #7 s #I7 t #>#-7 t #!|7 p #*Vc7 t # P7 t #7.7 s #(07 p #.:7 t #iW7 t #&]7 t #(07 t #MR7 p #+7 p #H>7  LL 7 m L ?Β L 7 m L ?Vf9L 7 m L ?%1L 7 m L ?Kp7L 7 m L ??@7L 7 m L ?FL 7 m L ?dNL 7 m L ?47L 7 m L ?N?L 7 m L ?dL 7 m L ?:% LL 7 m L ?8gL 7 m L ?; LL!7 m L ?4L%7 m L ? ZL7 m L ?4L7 m L ?57L7 m L ?FWbL7 m L ?8L7 m L ?/L7 m L ?:L7 m L ?HeL 7 m L ?!;L 7 m L ?G(L 7 m L ?L 7 m L ?L 7 m L ?CEL7 m L ?L7 m L ?TJL 7 m L ?7L 7 m L ?X3L 7 m L ?вʥ LL 7 m L ?L7 m L ?A  LL 7 m L ?;W L LL L L LL LL LL L)L LL L L LL L|L LL L L L%zL%zL%zLL L VL LL L L LL LL LL LL L L LL L L L$L LL LL LL L+L LL LL LL LL LL L L LL LL LL L L LL L L LL L L LL$L vL L LL L L L$L LL L&L L! L L%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zLLL" vL VL L# L L LL$ L&L LL% LL L%zL%zL%zL& L VLLLLLLLLLLL' v2LLL( v2L) 2LLL* v2-LLLLLL2 v-2)LLL4 v)2LL-L5 veLL)L6 vaL LLL8 vLLLLL-LB veL)LC aF LL-L-LP vLQ L-?9Q L?Zn#Q L?~1Q L?2Q L?"7>Q L?3U=Q L?Q L)?Q L? Q L?)Q L ?hQ L ?Q L? Q L?|FQ L?q Q L?/sQ L?!Q L?6 Q L ?a7Q L ?eQ L ?2aRQ Q 6L/`S7z _ZLLY VL] LLL!b L%b ^<s^8UL!fQLR^'^^QL)ULLL] L[ :^LLY VL] LLL!b L%b ^#s^UL!f s^^^QL^|LLl LY VL[ VLL|LY VL] LLL!b L%b^m L8L\ 2^ QL^/Ho#]tLT L#.Z!L^ L` LLfZ ^LX b d Lf Lw Lh LS L#.Z!z 7R z7S 7T E7U 7V T #9V7W V #O177X U #n7Y V #>37Z U # 7[ V #F7\ S #cV7] _ L%L/HoL#g%Z7^ a L%L/HoL#g%Z7` c L!L/HoL#g%Z7b e L!L/HoL#g%Z7d g L%L/HoL#g%Z7f i L%L/HoL#g%Z7h j Lk Ln LW Lo Lp Lq L%L/HoL#g%ZL/LL L/VL LL L)LdL/VzLr Ls LL/HoL#g%ZLt Lu L%L/HoL#g%ZLv Lx Ly Lz L ?z L ?xz L?z L?z L?rz L?=z L)?Uz L?&z L?O&z L ?z L?z L??z L-?z_(?z L ?9z L ?]tRz z 666 6L/`S7 _L^ ^^^& L)L "^L)L "^]L)L "^9LL!z9LL%z9LLz LL QLL L)L)L *RL  L  LL V%L r9L=LLL9LL-L 3R9L=!LL LL LLL9L vL LL L)L VL L)L VL LL Vk+Lg LL VLL L%j LL Vl^6LLf L aLw fL L vL ^ ^L/*LL^ ^^J^QLL쭥^QL L f  L L L VL L L%L)^iQL L f  L L L VL L L%j L l^$LLf L a L ^L/*LL-R^R_l^XLLf L aLuf6LL%L VL L L L LL9V^ ^L/*QRrL L-L-L-L966 #s%o675#&68# 69%#a16: #f6;!#6<!#76=%#!|6>#-X6?%#7.6@5#ua6A# 6B%#MR6C'#VK6D#6E#3U=6F5#}~&6G#3U=6H%#>36I #6J #W6K# 6L!#t,96M# 6N#KV6O# M6P #N6Q##f6R5#a16S%#O176T#x6U'#4}r6V#3U=6W #16X5#Edž46Y'#$6Z #۰126[#6\ # 6]%#(06^#6_#26`!#3U=6aILLLbLgLLhL-LLLkvLLLnvLL-LLqvLLLuvLLLxvL{L}LkEL~LLLLLQLLILCL!L>LLLLLLLLLLLLLLLvLL LLMLLzL>L LLL"LLMLLzL>LLL=LLzLCL%zL%zL%zL%zL%zL%zL%zL%zL%zLLLLLLLvLaV-LLL!b L%b^^QLLL.L\V^Rl^LLf L@aLL_:bL (I/b6LZg6bOLr)bh^^QL)ULLNLLVR^^bQL)ULLLLLVR^f^BQL)ULL]LLVR^F^"QL)ULLBLLVR^&^LL^LXLV^ L/*L ?׹L)?<L?DžL?4L?$0L?TL?5zL?4}rL-?hL ?M=LL?L?s%oR neko-2-4-0/boot/nekoml.n000066400000000000000000010324271464615675700151140ustar00rootroot00000000000000NEKOGQ@Nekoml_Main@IOIO@Neko_Printer neko/Printer@HashtblHashtbl@ListList@Nekoml_Type nekoml/Type@Neko_Compile neko/Compile@LexerLexer@StringString@ReflectReflect@Nekoml_Neko nekoml/Neko@Neko_Lexer neko/Lexer@Nekoml_Typer nekoml/Typer@SysSys@Nekoml_Lexer nekoml/Lexer@ZipZip@Neko_Parser neko/Parser@StackStack@Nekoml_Parser nekoml/Parser@ArgsArgs@Neko_Bytecode neko/Bytecode@CoreCore@Sys_exit@List_iter@String_split@Stack_dump@Neko_Printer_create@String_concat@List_rev@Neko_Bytecode_write@Nekoml_Neko_generate@Core_@compare@String_length@IO_close_out@IO_stderr@Neko_Compile_compile@Core_chr@Core_printf@Nekoml_Lexer_error_msg@Args_String@Core_ignore@String_unserialize@Zip_uncompress@Core_@pcons@Neko_Printer_print@Core_Neko_error@Core_throw@IO_write_file@Core_@empty@Nekoml_Typer_load_module@Nekoml_Typer_module_infos@Sys_version@Zip_init@Hashtbl_iter@List_append@IO_file_contents@Core_@print_union@Core_None@Lexer_line@Nekoml_Type_file_name@String_sub@Neko_Parser_error_msg@Args_Void@Neko_Lexer_error_msg@String_serialize@Stack_exc@Nekoml_Parser_error_msg@String_get@Nekoml_Typer_context@IO_printf@String_set@Core_Some@Zip_compress@Hashtbl_add@IO_write@Lexer_null_pos@Core_ord@Reflect_loader_path@Lexer_source@Neko_Compile_error_msg@Nekoml_Typer_error_msg@Core_string@Nekoml_Typer_verbose@Sys_without_extension@Hashtbl_create@Args_parse@List_map/ Exception : %s  %s(%d): %s H.nekoGenerating %s .n Compiling %s a FileNotFoundFileNotFoundNekoML Compiler v.B - (c)2005-2013 Haxe Foundation Usage : nekoml [options] files... nekoml.stdCached %s [%d bytes] S$ : additional file search path-pm: verbose mode-vu#: generate intermediate .neko files-n} : build module packages-pack : use this module package-usecore/: disable std lib-nostd%s not found in %s :8Mpnekoml/Main@IO@String@BufferBuffer@Core@Core_@print_union@String_blit@Buffer_add_char@String_create@String_sub@Core_assert@Core_ord@Core_Exit@Buffer_add_sub@Core_Neko_error@Buffer_string@Core_sprintf@Core_throw@String_length@String_get@Buffer_add@Buffer_create@Core_magic@Core_chr@Core_min@Core_invalid_argOverflowOverflowEofEofClosed&ClosedBlocked1Blocked@loadstd@< file_open file_contents file_close file_readfile_read_char file_writefile_write_char file_flushPrbrCFswbw  file_stdin file_stdout file_stderrCMUIO.inputbIO.read IO.read_buf core/IO.nml=) W ` i r     IO.write_i8  IO.output  o  IO.write_ui16  IO.write_i16  IO.write_ui24 4 = F O X a  IO@String@List@Buffer@Core@List_iter@Buffer_string@Core_sprintf@Buffer_add_char@Core_throw@Buffer_add@Buffer_create@Core_chr@Core_invalid_arg@Core_Not_found@Core_ord@string_splitstd@string_split  String.get  String.set  String.blit  String.sub* J j    \n\t\r\\\"\%.3d*  String.unescape<@serialize std@serialize@unserializestd@unserialize8@String@List@ArrayArray@Core@Core_@pcons@Array_make@Core_@aset@Array_list@Core_@compare@Array_create@Core_throw@Core_@empty@Array_sort@Core_invalid_arg@Core_Not_found@makeY@rmakeyList.hdList.tl6 List.iter2QHA[ List.chop~ List.loopIList.nth f  List@Array@Core@Core_@aset@Core_throw@Core_@aget@Core_Not_found[, ] @makeF!@merge_sortstd@merge_sortV![!i!!!!":"d""""#$asubE#v##Array@CoreNoneSome#NoneSome$ Neko_error"$Neko_error0$Invalid_argument=$Invalid_argumentK$Assert_failureX$Assert_failuref$Errorw$Error$ Stream_error$Stream_error Not_found$Not_foundExit$Exit@sprintf std@sprintf@string()${ __string = ; }8%%@compare &E' Array.getQ' Array.setr'@empty[]'@pcons :: '@cons''''''''(Core.int ( Core.float"(;(Core.chr>(V(^(f(s({(((((o:0(Core.stream_token(Core.stream_junk`))nekoCore@Buffer@Core@buffer_newstd@buffer_new@buffer_addstd@buffer_add@buffer_add_substd@buffer_add_sub@buffer_add_charstd@buffer_add_char@buffer_stringstd@buffer_string@buffer_resetstd@buffer_reset///Buffer@Neko_Printer@IO@List@String@Neko_Astneko/Ast@Core@IO_write_char@List_iter@IO_write_string@String_make@Core_@compare@Core_None@IO_close_out@IO_write@Neko_Ast_s_constant@Core_assert@IO_printf@Core_fstr5}555%s5E6{;U6 }(  ).%s,var t6while do if elsetrycatch %s function( %s return;return break;break continue $new(null)neko/Printer.nml%s => 6%s:switch  => k7 default => 7 Z=(>M>>neko/Printer@Neko_Ast@List@Lexer@String@Core@Core_@pcons@Core_Some@Core_@print_union@List_iter@Core_None@String_escape@Core_@empty@Core_string@List_mapTrueFalseNullThisIntFloatBuiltinIdent>TrueFalseNullThisIntd?Floatq?String~?Builtin?Ident?VarWhileDoIfElseFunctionReturnBreakContinueDefaultTryCatchSwitch?VarWhileDoIfElseFunctionReturnBreakContinueDefaultTryCatchSwitch SemicolonDotCommaArrow BraceOpen BraceClose ParentOpen ParentClose BracketOpen BracketCloseConstKeywordBinopComment CommentLineh@EofSemicolonDotCommaArrowBraceOpenBraceCloseParentOpenParentCloseBracketOpenBracketCloseConstUAKeywordbABinopoAComment|ACommentLineA NormalWhileDoWhileANormalWhileDoWhileEConstEBlock EParenthesisEFieldECallEArrayEVarsEWhileEIfETry EFunctionEBinopEReturnEBreak EContinueENextEObjectELabelESwitchAEConstBEBlockBEParenthesisBEFieldCECallCEArray*CEVars;CEWhileHCEIf\CETrypCEFunctionCEBinopCEReturnCEBreakCEContinueENextCEObjectCELabelCESwitchCD DD*DEDWDiD{DDDDD EH6HCHYHtruefalsenullthis"$JvarwhiledoiffunctionreturnbreakdefaultcatchswitchK=>/**///sKneko/Ast@Lexer@IO@List@String@LexEngine LexEngine@Core@Core_Some@List_array@Core_@print_union@String_blit@IO_input@String_create@Core_None@LexEngine_make_tables@String_sub@Core_assert@Core_fst@Core_@aget@Core_max@Core_Exit@Core_Neko_error@LexEngine_determinize@IO_read_string@Core_snd@String_length@String_get@Core_throw@LexEngine_parse@Core_@empty@Core_min@Core_invalid_arg@List_map Invalid_rule!LInvalid_rule,L9L=LALiLLLLLM MM+NNNcore/Lexer.nmlOPPLexer.empty_table#Q,QLexer@LexEngine@List@Hashtbl@Array@String@Core@List_array@Array_make@Core_@print_union@List_iter@Array_map@Hashtbl_find@Core_None@Core_assert@Array_sort@List_mem@List_filter@Core_fst@List_rev@Core_compare@Array_iteri@Core_@aset@Core_@compare@Array_iter@String_get@String_length@List_exists@Array_init@Core_Some@List_fold@Core_ignore@Hashtbl_add@Core_@aget@Core_ord@List_assoc@Core_@pcons@List_concat@Core_Neko_error@List_sort@Core_snd@Array_length@Core_throw@Core_@empty@List_map@List_append@Hashtbl_createEmptyMatchStarPlusNextChoiceVQEmptyMatchQStarQPlusQNextQChoiceQRcore/LexEngine.nmlRRuSSSTTTJTYTjTUUUtVxVVVVV\WtWW1XXXXdY{YYYYYYZZ[0[A[\[ InvalidRegexp[InvalidRegexp[[[[\#\\\&]_]~]^LexEngine@Hashtbl@Core@Core_throw@Core_Not_found?`G`P`X`v`````Hashtbl@Nekoml_Type@List@Hashtbl@Lexer@String@Core@String_set@Core_@print_union@List_iter@String_make@Hashtbl_find@String_sub@Core_assert@Hashtbl_add@String_concat@Core_ord@Core_@pcons@Core_sprintf@Core_Neko_error@String_get@String_length@Core_@empty@Core_chr@Core_string@Core_invalid_arg@List_exists@List_length@Hashtbl_create@List_phys@List_mapMutable ImmutablekMutableImmutable TAbstractTMonoTPolyTRecordTUnionTTupleTLinkTFunTNamedkTAbstractTMonoQlTPolyTRecord^lTUnionklTTuple|lTLinklTFunlTNamedlTVoidTIntTBoolTFloatTStringTCharTIdentTConstrTModulelTVoidTIntFmTBoolSmTFloat`mTStringmmTCharzmTIdentmTConstrmTModulemMRootMFailureMHandleMExecute MConstantsMFieldMTupleMToken MRecordFieldMJunkMSwitchMBindMWhenMNextmMRootMFailureMHandlenMExecutenMConstantsnMFieldnMTuplenMTokennMRecordFieldnMJunknMSwitchoMBind"oMWhen6oMNextGoTConstTBlock TParenthesisTCallTFieldTArrayTVarTIf TFunctionTBinop TTupleDecl TTypeDeclTMut TRecordDecl TListDeclTUnopTMatchTTry TTupleGet TErrorDeclTWhileXoTConstpTBlockpTParenthesispTCallpTFieldpTArraypTVarpTIfpTFunction qTBinop!qTTupleDecl5qTTypeDeclBqTMutOqTRecordDecl\qTListDecliqTUnopvqTMatchqTTryqTTupleGetqTErrorDeclqTWhileqqq=rHrVr\rfrvoidintfloatcharerrorstringboolrrssRsZsssssssNekoml.Type.file_nametttmutable u '_%snekoml/Type.nml'%s{ %s } : =uhu(%s) -> ) u>xxxxzzznekoml/Type@Neko_Compile@Hashtbl@List@Lexer@Array@MapMap@String@Neko_Ast@Neko_Bytecode@Core@Neko_Bytecode_op_param@Neko_Ast_Builtin@Neko_Bytecode_SetIndex@Neko_Bytecode_trap_stack_delta@Neko_Bytecode_GlobalFloat@List_rev@Neko_Bytecode_Mod@Neko_Bytecode_Bool@Neko_Bytecode_Apply@Neko_Bytecode_MakeEnv@String_length@Array_iter@Neko_Bytecode_SetGlobal@Neko_Bytecode_Lte@Neko_Bytecode_Neq@Neko_Bytecode_AccIndex0@Array_set@Neko_Bytecode_AccIndex1@Neko_Bytecode_Or@Hashtbl_length@Core_Neko_error@Neko_Ast_EField@Core_snd@Neko_Bytecode_Not@Neko_Bytecode_GlobalString@Neko_Bytecode_AccEnv@Neko_Bytecode_SetThis@Core_@empty@Neko_Bytecode_ObjCall@Neko_Bytecode_Jump@List_length@Array_make@Core_@print_union@Neko_Bytecode_AccInt@Core_assert@String_sub@Array_sort@Neko_Bytecode_New@Neko_Bytecode_Pop@Neko_Bytecode_JumpTable@Neko_Ast_Ident@Array_add@Neko_Bytecode_PhysCompare@Neko_Bytecode_AccGlobal@Neko_Bytecode_Div@Neko_Bytecode_Ret@Hashtbl_add@Neko_Bytecode_Xor@Core_Exit@Neko_Bytecode_GlobalVersion@Neko_Bytecode_TypeOf@Array_length@Neko_Bytecode_SetField@Neko_Bytecode_Lt@Array_append@Neko_Bytecode_JumpIf@Neko_Bytecode_AccThis@Map_iter@Hashtbl_create@Neko_Bytecode_JumpIfNot@List_iter@Neko_Bytecode_SetEnv@Map_find@Map_add@Neko_Bytecode_Hash@Array_map@Neko_Bytecode_EndTrap@Neko_Ast_EConst@Neko_Bytecode_AccStack@Hashtbl_exists@Neko_Bytecode_Shl@Core_@aset@Core_compare@Neko_Ast_EBlock@Neko_Bytecode_GlobalDebug@Core_@compare@Neko_Bytecode_SetArray@Neko_Ast_EVars@Neko_Ast_EBinop@Neko_Bytecode_Eq@Neko_Ast_EIf@Neko_Bytecode_AccTrue@Core_ignore@Neko_Bytecode_Gte@Neko_Ast_Int@Neko_Bytecode_Sub@Core_@pcons@Neko_Bytecode_TailCall@Core_sprintf@List_sort@Core_throw@Neko_Ast_ECall@Neko_Bytecode_Mult@Map_empty@Neko_Bytecode_AccField@Neko_Bytecode_AccIndex@Hashtbl_iter@Neko_Bytecode_Shr@Hashtbl_find@Core_None@Array_create@Neko_Bytecode_UShr@Neko_Bytecode_IsNotNull@Core_fst@Neko_Bytecode_AccFalse@Neko_Bytecode_AccNull@Neko_Bytecode_Push@Neko_Bytecode_SetStack@Neko_Ast_iter@Neko_Bytecode_MakeArray@Neko_Bytecode_AccArray@Neko_Bytecode_Compare@Neko_Bytecode_AccBuiltin@Neko_Bytecode_Call@Core_Some@Neko_Bytecode_IsNull@Neko_Ast_This@Lexer_null_pos@Neko_Bytecode_And@Neko_Bytecode_GlobalVar@Neko_Bytecode_AccStack0@Neko_Bytecode_Add@Neko_Bytecode_AccStack1@Neko_Bytecode_Loop@Neko_Bytecode_GlobalFunction@Core_string@Neko_Bytecode_Trap@List_map@Neko_Bytecode_GtXEnvXStackXGlobalXFieldXIndexXArrayXThisXEnvXStackXGlobalXFieldXIndexXArrayXThisʂErrorՂStack alignment failure҄2YÆۆ>{Break outside a loopContinue outside a looparray"A0applyOn0Variable declaration must be done inside a blockneko/Compile.nml=։2Label is not supported in this part of the programDuplicate label ==-qtnulltinttfloattbooltstringtobjecttarray tfunction tabstractNInvalid access+Ö+-*%<<>>>>>|&^!=>>=<<=Unknown operation&&||++=--=+=-=/=*=%=<<=>>=>>>=|=&=^=%MristruenottypeofhashnewcomparepcomparegotoUnknown label athrowϣInvalid $goto statement aconcatäܤcall$tmp*o:7Label failure %d %d %s %spw T_#3Clneko/Compile@Map@Core@Core_compare@Core_@print_union@Core_@compare@Core_throw@Core_assert@Core_Not_found@Core_invalid_arg@Core_maxNodedEmptyNodeۿ core/Map.nmlMap.remove_min_bindings4<EUDo* 2Map@Neko_Bytecode@Hashtbl@IO@List@Array@String@Buffer@Core@Array_make@IO_read_byte@Core_@print_union@List_iter@Array_create@Hashtbl_find@String_make@IO_write_i32@Core_assert@IO_read_ui16@IO_read_i32@Array_iteri@Core_@aset@IO_read@IO_write_ui16@IO_write_string@Array_iter@String_length@Buffer_create@Core_invalid_arg@Array_get@IO_printf@IO_read_char@Array_init@Array_add@IO_write_char@Buffer_add_char@Array_set@IO_write_byte@Hashtbl_add@IO_write@Core_@aget@Core_@pcons@Core_Neko_error@Buffer_string@Core_sprintf@String_escape@Array_length@Core_throw@Core_@empty@Core_string@Core_error@Hashtbl_createAccNullAccTrueAccFalseAccThisAccIntAccStack AccGlobalAccEnvAccFieldAccArrayAccIndex AccBuiltinSetStack SetGlobalSetEnvSetFieldSetArraySetIndexSetThisPushPopCallObjCallJumpJumpIf JumpIfNotTrapEndTrapRetMakeEnv MakeArrayBoolIsNull IsNotNullAddSubMultDivModShlShrUShrOrAndXorEqNeqGtGteLtLteNotTypeOfCompareHashNew JumpTableApply AccStack0 AccStack1 AccIndex0 AccIndex1 PhysCompareTailCallLoopAccNullAccTrueAccFalseAccThisAccIntAccStackAccGlobalAccEnvAccFieldAccArrayAccIndexAccBuiltinSetStackSetGlobalSetEnvSetField!SetArraySetIndex.SetThisPushPop;CallHObjCallUJumpbJumpIfoJumpIfNot|TrapEndTrapRetMakeEnvMakeArrayBoolIsNullIsNotNullAddSubMultDivModShlShrUShrOrAndXorEqNeqGtGteLtLteNotTypeOfCompareHashNewJumpTableApplyAccStack0AccStack1AccIndex0AccIndex1PhysCompareTailCallLoop GlobalVarGlobalFunction GlobalString GlobalFloat GlobalDebug GlobalVersionGlobalVarIGlobalFunctionVGlobalStringgGlobalFloattGlobalDebugGlobalVersion Invalid_fileInvalid_fileField hashing conflict  and Neko.Bytecode.write_debug_infos?TNEKOpnk"Neko.Bytecode.read_debug_infosKNeko.Bytecode.loopT[ neko/Bytecode.nmlnglobals : %d  nfields : %d codesize : %d ops , %d total  GLOBALS =  global %d : %s  function  nargs string "float debug %d ops %d bytesversion  FIELDS =  %s%s%.8X CODE = %.6X %6d %s  AccField  AccBuiltin  SetField END neko/Bytecode@Reflect@List@Core@Core_@print_union@Core_magic@Core_invalid_arg@List_mapVNullVIntVFloatVBoolVStringVObject VAbstract VFunctionVArrayVNullVIntoVFloat|VBoolVStringVObjectVAbstractVFunctionVArray@module_readstd@module_read@module_namestd@module_name@module_exportsstd@module_exports@module_loaderstd@module_loader@module_execstd@module_exec@module_nglobalsstd@module_nglobals@module_global_getstd@module_global_get@module_global_setstd@module_global_set@module_code_sizestd@module_code_size@module_read_pathstd@module_read_path Reflect.value|!5@NReflect@Nekoml_Neko@Nekoml_Typer@Hashtbl@List@IO@Nekoml_Type@Lexer@Array@Neko_Parser@SetSet@String@Neko_Ast@Buffer@Core@Nekoml_Typer_module@Array_list@Set_add@List_iter@Nekoml_Type_TIdent@String_split@String_concat@Neko_Ast_Builtin@Neko_Ast_EConst@Neko_Ast_True@Neko_Ast_String@Neko_Ast_ESwitch@Nekoml_Type_t_string@List_rev@Neko_Ast_EBlock@Core_compare@Core_@compare@IO_read_string@Neko_Ast_EVars@Lexer_input@Neko_Ast_EBinop@Nekoml_Type_mk@Neko_Ast_EIf@Lexer_create@Neko_Ast_EWhile@Neko_Ast_Int@Neko_Ast_Float@Nekoml_Type_t_error@Neko_Ast_Null@Core_@pcons@Core_Neko_error@Neko_Ast_EField@Nekoml_Typer_union@Core_snd@List_sort@Neko_Ast_ECall@Core_@empty@Nekoml_Type_MRoot@Nekoml_Type_TModule@List_length@List_phys@Hashtbl_iter@List_append@Core_@print_union@Nekoml_Type_tlinks@Neko_Ast_ETry@Neko_Parser_parse@Hashtbl_find@Core_None@Core_assert@Nekoml_Type_file_name@Core_fst@Set_remove@Neko_Ast_EFunction@Nekoml_Type_TListDecl@Neko_Ast_NormalWhile@Nekoml_Type_MFailure@Neko_Ast_EReturn@Nekoml_Type_TString@Buffer_create@Neko_Ast_ELabel@Neko_Ast_Ident@Hashtbl_hash@Neko_Ast_ENext@Core_Some@Array_init@Neko_Ast_EArray@Neko_Ast_False@Nekoml_Type_TConstr@Hashtbl_add@Nekoml_Type_MExecute@Neko_Ast_EParenthesis@Lexer_null_pos@Core_ord@Set_empty@Nekoml_Type_TConst@Nekoml_Type_MSwitch@Nekoml_Type_TCall@Nekoml_Type_is_int@Core_string@Set_exists@Nekoml_Type_t_void@List_map@Hashtbl_createNative StructuralNativeStructurallKvi@nekoml/Neko.nmln"d::@empty@cons@exc stream_pos@posrethrow% stream_token stream_junkL  p     @print_unionM@compare;`andorxor===!==:=@asetidivneko'@pcons@aget@tmp}!"=#G$$%exportsloader loadmodule/&&&L'nekoml/Neko@Nekoml_Typer@Hashtbl@List@IO@Nekoml_Type@Nekoml_Ast nekoml/Ast@Nekoml_Match nekoml/Match@Array@Lexer@Map@String@Set@Nekoml_Parser@Buffer@Core@Array_list@Nekoml_Type_polymorphize@String_concat@Nekoml_Type_TLink@Nekoml_Ast_EType@Nekoml_Type_TWhile@List_rev@Nekoml_Type_t_string@Nekoml_Type_TErrorDecl@Nekoml_Type_duplicate@String_length@Core_print@Nekoml_Ast_PRecord@Core_magic@Lexer_input@Nekoml_Ast_ETypeDecl@Nekoml_Type_TField@Nekoml_Ast_ATyped@Core_printf@Nekoml_Ast_EBlock@Lexer_create@List_iter2@Nekoml_Type_generator@Nekoml_Type_TTypeDecl@Core_@aget@Nekoml_Ast_Int@Core_Neko_error@Nekoml_Type_TFunction@Core_snd@Core_@empty@Nekoml_Ast_PConstr@Nekoml_Type_TModule@Nekoml_Type_TVoid@Nekoml_Type_TMut@List_length@List_append@Nekoml_Type_TIf@Nekoml_Type_TTry@Nekoml_Ast_SMagicExpr@Core_@print_union@Nekoml_Type_tlinks@Nekoml_Type_TNamed@Nekoml_Ast_Ident@Nekoml_Type_TInt@Nekoml_Match_make@Core_assert@Nekoml_Type_mk_record@Nekoml_Type_file_name@Nekoml_Type_TArray@Nekoml_Type_t_abstract@Set_union@Nekoml_Match_error_ref@Hashtbl_remove@String_get@Buffer_create@List_fold@Nekoml_Type_TRecordDecl@Hashtbl_add@Nekoml_Ast_PStream@Set_empty@IO_read_file@Nekoml_Type_genid@Nekoml_Type_t_bool@Nekoml_Type_t_char@Nekoml_Ast_EAbstract@Nekoml_Type_TConst@Nekoml_Type_TBlock@Nekoml_Ast_SPattern@Hashtbl_create@Nekoml_Type_t_float@Nekoml_Type_t_void@Map_iter@Nekoml_Ast_ETuple@Nekoml_Type_t_polymorph@Nekoml_Type_TChar@Set_add@Nekoml_Type_s_context@List_iter@Map_add@Map_find@Nekoml_Type_TIdent@Nekoml_Type_TBinop@Nekoml_Type_s_type@List_find@Nekoml_Ast_EConst@Nekoml_Ast_PTyped@Nekoml_Ast_EUnion@Hashtbl_exists@Nekoml_Type_TTupleDecl@List_all@Core_@compare@IO_read_string@Nekoml_Type_mk@Nekoml_Type_TParenthesis@List_exists@Nekoml_Ast_ECall@Nekoml_Ast_String@Core_ignore@Nekoml_Ast_ETupleDecl@Nekoml_Type_TFloat@Nekoml_Match_fully_matched_ref@Nekoml_Type_t_error@Nekoml_Type_mk_union@Core_@pcons@List_hd@Core_throw@Nekoml_Type_TMono@Nekoml_Ast_ETupleGet@List_nth@Nekoml_Ast_ANamed@Nekoml_Ast_PTuple@Map_empty@Hashtbl_iter@Nekoml_Type_Immutable@Nekoml_Type_Mutable@Hashtbl_find@Core_None@Nekoml_Type_t_int@Nekoml_Type_TTupleGet@Core_fst@Hashtbl_replace@Nekoml_Parser_parse@Nekoml_Type_TVar@Nekoml_Type_t_mono@Nekoml_Type_mk_fun@Nekoml_Type_TUnop@Nekoml_Ast_PIdent@Nekoml_Type_TString@Set_diff@Nekoml_Type_TMatch@Set_iter@Core_Some@Array_init@Nekoml_Type_t_poly@Nekoml_Ast_EVar@Nekoml_Ast_s_path@Nekoml_Type_TConstr@Lexer_null_pos@Nekoml_Ast_EPoly@Nekoml_Type_TBool@Nekoml_Ast_PAlias@Nekoml_Type_mk_tup@Nekoml_Type_TCall@Nekoml_Type_etype@Core_string@List_split@Set_exists@Nekoml_Type_pos@List_map Cannot_unify Have_no_field Unknown_fieldModule_not_loadedCustom\(Cannot_unify(Have_no_field(Stack(Unknown_field(Module_not_loaded(Custom ))Error") Cannot unify  have no field  Unknown field Module  require an interface3))**(*nekoml/Typer.nml7*C*d*k*t*** Unknown type (++Unknown constructor ,,Unknown identifier 1--?.."/o/.00011122&Invalid number of type parameters for 5Unbound type variable '566|9NIntNFloatNStringNNanN;NIntNFloatNStringNNan;7<P<Field  is not mutablereflistInvalid operation \< K"K@tZLLlMMInvalid % sequence}OTyping  jRR{SSS4Topenassert invalid_argTYPE%s:(%d): type : %s format#Constant string required for formatToo many arguments@TV0Variable declaration not allowed outside a blockW&Invalid number of parameters for type "Invalid type redefinition of type W WqWW Duplicate declaration for field WXMissing field  in record declarationXXYThis matching is not complete)Y3YrYYhii,This variable is several time in the pattern{jkk$Constructor does not take parametersConstructor require parametersstreamkkm Variable  must occur in all patternsss1t@t%uuUvkvv.nmlFile not found  ww anonymous Parsed %s  xTyping done with %s 4x]z~zaznekoml/Typer@Nekoml_Ast@Lexer@String@Core@Core_@print_union@Core_snd@String_escape@Core_string@String_concat@String_escape_charCharConstrModule |Int|Char|Bool|Float|String|Ident|Constr|Module|TypeThenWhen Exception }VarIfElseFunctionTryCatchTypeMatchThenWhenWhileExceptionQuoteVertical StreamOpen StreamClose}EofSemicolonDotCommaQuoteBraceOpenBraceCloseParentOpen~ParentCloseBracketOpenBracketCloseArrowVerticalStreamOpenStreamCloseConst~Keyword~Binop CommentCommentLine$ETypeEPolyETupleEArrow1ETypevEPolyETupleEArrow EAbstractEAliasERecordEUnionEAbstractEAliasERecordEUnionATypedANamedATuple!ATypedXANamediATuplevSPatternSExpr SMagicExprSPatternSExprǀSMagicExpr؀PIdentPConstPTuplePRecordPConstrPAliasPTypedPStreamPIdentfPConstsPTuplePRecordPConstrPAliasPTypedÁPStreamԁ):EVarEUnop ETypeAnnot ETupleDecl ETypeDecl EErrorDecl ERecordDeclEMatch ETupleGetEApplyKEConstpEBlock}EFieldECallEArrayEVarEIf΃EFunctionEBinopEUnopETypeAnnot!ETupleDecl2ETypeDecl?EErrorDeclSERecordDecldEMatchqETryETupleGetEApplyEWhileƄntypematchthenwhen exception->[<>]*--nekoml/Ast@Nekoml_Match@List@Nekoml_Type@Nekoml_Ast@Core@Nekoml_Type_TChar@Core_@print_union@Nekoml_Ast_Ident@Core_None@Nekoml_Type_TIdent@Nekoml_Type_MRecordField@Nekoml_Ast_PConst@Nekoml_Type_TInt@Core_assert@Nekoml_Ast_PTyped@Nekoml_Type_MField@Core_fst@List_rev@Nekoml_Type_MFailure@Core_@compare@Nekoml_Type_MHandle@Nekoml_Ast_PRecord@Nekoml_Type_TString@Nekoml_Type_MTuple@Core_magic@Nekoml_Type_mk@Core_Some@List_fold@Nekoml_Type_TConstr@Nekoml_Type_MExecute@Nekoml_Ast_PStream@Nekoml_Type_TFloat@Nekoml_Type_MToken@Nekoml_Type_MJunk@Nekoml_Type_MBind@Nekoml_Type_TBool@List_assoc@Core_@pcons@List_concat@Nekoml_Ast_PAlias@Core_Neko_error@Nekoml_Type_MSwitch@Core_snd@Nekoml_Type_TConst@Nekoml_Type_MConstants@Nekoml_Type_MWhen@Nekoml_Ast_PConstr@Core_@empty@Nekoml_Type_MRoot@Nekoml_Type_MNext@Nekoml_Ast_PTuple@Nekoml_Type_TVoid@Nekoml_Type_TModule@List_map@List_length@List_append@Nekoml_Type_t_voidTotalPartialDubious]TotalPartialDubiousnekoml/Match.nml܌Xb3Y#zDW" ^$Some pattern are never matchedThis pattern is never matchednekoml/Match@Set@Core@Core_compare@Core_@print_union@Core_throw@Core_assert@Core_Not_found@Core_maxEmptyNode̥  core/Set.nml4 |ߨ/ƩΪ!(.^#*aSet@Nekoml_Parser@Nekoml_Lexer@Nekoml_Ast@Lexer@Core@Nekoml_Ast_EMatch@Nekoml_Ast_ETypeAnnot@Lexer_punion@Nekoml_Ast_EConst@Nekoml_Ast_EUnion@Nekoml_Ast_PTyped@Nekoml_Ast_EUnop@Nekoml_Ast_EType@Core_Stream_error@Nekoml_Ast_EArrow@Nekoml_Ast_EBinop@Nekoml_Ast_ETypeDecl@Nekoml_Ast_PRecord@Nekoml_Ast_ECall@Nekoml_Ast_EApply@Nekoml_Ast_ATyped@Nekoml_Ast_EBlock@Nekoml_Ast_EWhile@Nekoml_Ast_s_token@Nekoml_Ast_EFunction@Nekoml_Ast_EErrorDecl@Nekoml_Ast_ETupleDecl@Nekoml_Ast_SExpr@Core_@pcons@Nekoml_Ast_EField@Nekoml_Ast_Int@Nekoml_Ast_Module@Nekoml_Ast_ERecordDecl@Core_Neko_error@Core_snd@Nekoml_Ast_Constr@Nekoml_Ast_EAlias@Core_throw@Nekoml_Ast_PConstr@Core_@empty@Nekoml_Ast_PTuple@Nekoml_Ast_ANamed@Nekoml_Ast_EIf@Core_@print_union@Core_stream_junk@Core_None@Nekoml_Ast_Ident@Core_stream@Nekoml_Ast_PConst@Core_assert@Core_fst@Nekoml_Lexer_token@Nekoml_Ast_ATuple@Nekoml_Ast_ETry@Core_stream_pos@Nekoml_Ast_PIdent@Core_stream_token@Nekoml_Ast_ERecord@Core_Some@Nekoml_Ast_EArray@Nekoml_Ast_EVar@Nekoml_Ast_pos@Nekoml_Ast_PStream@Nekoml_Ast_EPoly@Lexer_null_pos@Nekoml_Ast_EAbstract@Nekoml_Ast_PAlias@Nekoml_Ast_SPattern@Nekoml_Ast_Eof@Lexer_token@Nekoml_Ast_ETuple UnexpectedUnclosedUnexpectedUnclosedError Unexpected  Unclosed Q<>`Cm zθݸwRnekoml/Parser.nmlFYgNCurecrhxxJ%tLmutable_las q  nekoml/Parser@Nekoml_Lexer@Hashtbl@List@Nekoml_Ast@Lexer@String@Neko_Lexer@Buffer@Core@List_iter@Nekoml_Ast_Var@Lexer_punion@Lexer_char@Nekoml_Ast_Float@Nekoml_Ast_Quote@Nekoml_Ast_Keyword@Nekoml_Ast_Char@Nekoml_Ast_Then@Lexer_current@String_length@Core_chr@Nekoml_Ast_String@Nekoml_Ast_Binop@Neko_Lexer_ecomment@Nekoml_Ast_Vertical@Nekoml_Ast_Try@Nekoml_Ast_ParentOpen@Nekoml_Ast_BracketClose@Nekoml_Ast_Match@Nekoml_Ast_Arrow@String_escape_char@Buffer_reset@Nekoml_Ast_StreamOpen@Core_@pcons@Nekoml_Ast_Int@Core_int@Neko_Lexer_estring@Core_sprintf@Core_Neko_error@Lexer_build@Nekoml_Ast_Constr@Core_throw@Nekoml_Ast_Bool@Nekoml_Ast_Catch@Core_@empty@Nekoml_Ast_CommentLine@Core_@print_union@Nekoml_Ast_Ident@Hashtbl_find@Nekoml_Ast_Semicolon@String_sub@Nekoml_Ast_Const@Nekoml_Ast_BraceClose@Nekoml_Ast_Function@Nekoml_Ast_Dot@Nekoml_Ast_Comma@String_get@Nekoml_Ast_BraceOpen@Nekoml_Ast_BracketOpen@Nekoml_Ast_Else@Nekoml_Ast_Exception@Hashtbl_add@Lexer_empty@Nekoml_Ast_ParentClose@Nekoml_Ast_If@Lexer_data@Buffer_string@Nekoml_Ast_StreamClose@Nekoml_Ast_Type@Nekoml_Ast_When@Nekoml_Ast_Comment@Lexer_token@Nekoml_Ast_Eof@Lexer_curpos@Hashtbl_create@Nekoml_Ast_s_keyword@Nekoml_Ast_WhileInvalid_characterUnterminated_stringUnclosed_commentInvalid_escaped_characterInvalid_escapeInvalid_characterkUnterminated_stringUnclosed_commentInvalid_escaped_characterxInvalid_escapeErrorInvalid character '%s'Unterminated stringUnclosed commentInvalid escaped character %dInvalid escape sequencew[a-z_][a-zA-Z0-9_]*[A-Z][a-zA-Z0-9_]*[-!=*/<>&|%+:][0-9][ ]+ /:\[EP[f0x[0-9a-fA-F]+r/\*'\\n''\\t''\\r''\\'''\\\\''\\"''[^\\]'0'\\[0-9][0-9][0-9]'z //[^ ]* ?\[<?nekoml/Lexer@Neko_Lexer@Hashtbl@List@Lexer@String@Neko_Ast@Buffer@Core@List_iter@Neko_Ast_Default@Neko_Ast_Keyword@Neko_Ast_Semicolon@Neko_Ast_BraceOpen@Neko_Ast_Function@Neko_Ast_ParentOpen@Neko_Ast_BracketClose@Neko_Ast_Builtin@Lexer_punion@Neko_Ast_True@Lexer_char@Neko_Ast_String@Neko_Ast_Return@Neko_Ast_Continue@Neko_Ast_While@Lexer_current@String_length@Neko_Ast_BracketOpen@Core_chr@Neko_Ast_Try@Neko_Ast_Int@Neko_Ast_Float@Neko_Ast_BraceClose@Neko_Ast_ParentClose@Neko_Ast_Null@Buffer_reset@Core_@pcons@Core_int@Core_sprintf@Core_Neko_error@Neko_Ast_Switch@Lexer_build@Core_throw@Neko_Ast_Do@Core_@empty@Neko_Ast_Binop@Neko_Ast_s_keyword@Core_@print_union@Hashtbl_find@Neko_Ast_Dot@Neko_Ast_Arrow@String_sub@Neko_Ast_If@Neko_Ast_Else@Neko_Ast_Catch@String_get@Buffer_add@Neko_Ast_Ident@Buffer_add_char@Neko_Ast_Break@Neko_Ast_Const@Neko_Ast_False@Neko_Ast_This@Hashtbl_add@Lexer_empty@Neko_Ast_Comma@Core_ord@Neko_Ast_Comment@Core_Exit@Lexer_data@Neko_Ast_Eof@Buffer_string@Neko_Ast_Var@Neko_Ast_CommentLine@Lexer_token@Lexer_curpos@Hashtbl_create Unclosed_nxmlbInvalid_characterUnterminated_stringUnclosed_commentUnclosed_nxmlInvalid_escaped_characterInvalid_escapeErrorInvalid character '%c'Invalid character 0x%.2X Unclosed nxmlz      [a-zA-Z_@][a-zA-Z0-9_@]*[-!=\*/<>&|^%\+:]= I U a Continuel y           [ ]+[0-9]+ [0-9]+.[0-9]*.[0-9]+ ' 7 G W g o  f      : \*/= \*W [^*]+q z \\" \\\\ \\n \\t \\r \\[0-9][0-9][0-9]/:=[^\\"]+W`m[^<]+neko/Lexer@Neko_Parser@IO@Neko_Binast neko/Binast@Lexer@Neko_Xmlneko/Xml@Neko_Ast@Neko_Lexer@Buffer@Core@Core_@print_union@Core_stream_junk@Neko_Ast_ETry@Core_None@Neko_Ast_Keyword@Neko_Ast_Default@Core_stream@Neko_Xml_parse_string@Lexer_punion@Neko_Ast_EConst@Core_fst@Neko_Ast_ESwitch@Neko_Ast_EFunction@Neko_Ast_NormalWhile@Neko_Ast_DoWhile@Neko_Ast_EBlock@Core_Stream_error@Core_stream_pos@Neko_Ast_EObject@IO_read_string@Neko_Ast_s_token@Neko_Ast_EVars@Neko_Ast_EReturn@Buffer_create@Lexer_input@Neko_Ast_EBinop@Core_stream_token@Neko_Ast_ELabel@Neko_Ast_pos@Neko_Ast_EContinue@Neko_Ast_Ident@Core_Some@Neko_Ast_EWhile@Neko_Ast_EIf@Neko_Ast_EArray@Lexer_create@Neko_Ast_Int@Neko_Ast_EParenthesis@Lexer_null_pos@Neko_Binast_parse_from_string@Core_@pcons@Neko_Ast_Eof@Neko_Ast_EField@Core_Neko_error@Core_snd@Core_throw@Neko_Ast_ECall@Neko_Lexer_expr@Core_string@Core_@empty@Neko_Ast_EBreak@Lexer_token Invalid_nxmlg-Unexpected-Unclosed-Invalid_nxml--Error-Invalid nxml (-).8.90A00 2+3AiDFGiIKL8MMlOOSPPneko/Parser@Neko_Binast@IO@Lexer@Array@Neko_Ast@Core@IO_read_byte@Neko_Ast_ETry@Core_None@Array_create@Core_assert@Neko_Ast_Builtin@IO_read_ui16@Neko_Ast_True@Neko_Ast_EConst@Core_fst@IO_read_i32@Neko_Ast_String@Neko_Ast_ESwitch@Neko_Ast_EFunction@IO_read@Neko_Ast_EBlock@Neko_Ast_NormalWhile@Neko_Ast_DoWhile@Neko_Ast_EObject@IO_read_ui24@Neko_Ast_EVars@Neko_Ast_EReturn@Array_get@Neko_Ast_EBinop@Neko_Ast_ELabel@Neko_Ast_Ident@Neko_Ast_EContinue@Neko_Ast_ENext@Core_Some@Array_add@Neko_Ast_EArray@Neko_Ast_EWhile@Neko_Ast_EIf@Neko_Ast_False@Neko_Ast_Int@Neko_Ast_This@Neko_Ast_Float@Neko_Ast_EParenthesis@Neko_Ast_Null@Core_@pcons@Neko_Ast_EField@Array_length@Neko_Ast_ECall@Core_@empty@Neko_Ast_EBreak@Core_errorneko/Binast.nmlQNBA+QtQInvalid const tag : QInvalid op tag PRySSSSSInvalid expr tag : TInvalid binast headerWneko/Binast@Neko_Xml@List@Lexer@String@Neko_Ast@XmlXml@Core@List_iter@String_split@String_concat@Neko_Ast_Builtin@Neko_Ast_EConst@Neko_Ast_True@List_rev@Neko_Ast_String@Neko_Ast_ESwitch@Neko_Ast_EBlock@Core_@compare@Xml_attrib@String_unescape@String_length@Neko_Ast_EVars@Neko_Ast_EBinop@Neko_Ast_EWhile@Neko_Ast_EIf@Neko_Ast_Int@Neko_Ast_Float@Xml_Node@Neko_Ast_Null@Core_int@Core_@pcons@Core_Neko_error@Neko_Ast_EField@Neko_Ast_ECall@Core_throw@Core_@empty@Xml_node_text@List_append@Xml_node_name@Core_@print_union@Neko_Ast_ETry@Core_None@Core_assert@String_sub@Xml_firstNode@Neko_Ast_EFunction@Neko_Ast_NormalWhile@Neko_Ast_DoWhile@Neko_Ast_EObject@String_get@Neko_Ast_EReturn@Neko_Ast_s_constant@Core_invalid_arg@Neko_Ast_ELabel@Xml_nodes@Neko_Ast_Ident@Neko_Ast_EContinue@Neko_Ast_ENext@Core_Some@Neko_Ast_EArray@Xml_parse@Neko_Ast_False@Neko_Ast_This@Neko_Ast_EParenthesis@Lexer_null_pos@Neko_Ast_EBreak@Core_string@List_map"ZError-Z neko/Xml.nml:ZFZNeko.Xml.errorL[U[ifsbgconextobjectlabel[9\F\T\b\case\Unknown node name : ]Neko.Xml.parse'd}ddeYeqlneko/Xml@Xml@List@IO@String@Buffer@Core@Core_@print_union@List_iter@List_find@IO_write@String_concat@List_filter@List_hd@String_lowercase@Buffer_string@IO_write_string@Core_snd@String_escape@Buffer_add@Buffer_create@Core_invalid_arg@IO_printf@List_mapPCDataCDataDocumentlNodelPCDatalCDatamDocument mmError%m@parsero:02mEmXmmm@parse std@parse_xml$nlnnn Xml.firstNoden Xml.nodesn Xml.node_name@o Xml.node_textdooo Xml.attribp<%s%s="Ep/> epDqXml@Sys@List@Array@String@Core@Array_make@Core_Some@String_split@Core_None@String_concat@Core_assert@List_rev@sys_exit std@sys_exit@get_env std@get_env@get_cwd std@get_cwd@exe_pathstd@sys_exe_path@sys_file_typestd@sys_file_type@sys_read_dirstd@sys_read_dir}]~\ core/Sys.nml~~std@sys_exists std@put_env* std@set_cwd0dir6BSys@Zip@String@Buffer@Core@Buffer_add_sub@Core_@print_union@Buffer_string@String_create@Core_None@String_length@Core_throw@Core_string@String_sub@Buffer_create@Core_ErrorNOSYNCFULLFINISHBLOCK܀NOSYNCFULLFINISHBLOCK@set_flush_modezlib@set_flush_mode@inflate_initzlib@inflate_init@deflate_bufferzlib@deflate_buffer@inflate_bufferzlib@inflate_buffer@deflate_initzlib@deflate_init@deflate_boundzlib@deflate_bound@deflate_endzlib@deflate_end@inflate_endzlib@inflate_end/ˁӁ4<KCompression failedZȂZip@Stack@IO@Array@Core@Core_@print_union@IO_stdout@Array_iter@IO_write@IO_printf CFunctionPosńCFunctionModulePos @make_stackCalled from a C function $Called from %s (no debug available) Called from %s line %d Stack@Args@List@Sys@Hashtbl@Array@Core@Sys_exit@Core_int@Core_@print_union@List_iter@Core_Neko_error@Hashtbl_find@Array_length@Core_throw@Core_print@Hashtbl_add@Sys_args@Core_@aget@Core_printf@Hashtbl_createVoidVoidStringIntInvalidInvalid Options :  %s %s 7V-help--helpbxInvalid argument : SArgsBnekoml/Mainnekoml/Main.nmlIOcore/IO.nmlStringcore/String.nmlListcore/List.nmlArraycore/Array.nmlCorecore/Core.nmlBuffercore/Buffer.nmlneko/Printerneko/Printer.nmlneko/Astneko/Ast.nmlLexercore/Lexer.nmlLexEnginecore/LexEngine.nmlHashtblcore/Hashtbl.nmlnekoml/Typenekoml/Type.nmlneko/Compileneko/Compile.nmlMapcore/Map.nmlneko/Bytecodeneko/Bytecode.nmlReflectcore/Reflect.nmlnekoml/Nekonekoml/Neko.nmlnekoml/Typernekoml/Typer.nmlnekoml/Astnekoml/Ast.nmlnekoml/Matchnekoml/Match.nmlSetcore/Set.nmlnekoml/Parsernekoml/Parser.nmlnekoml/Lexernekoml/Lexer.nmlneko/Lexerneko/Lexer.nmlneko/Parserneko/Parser.nmlneko/Binastneko/Binast.nmlneko/Xmlneko/Xml.nmlXmlcore/Xml.nmlSyscore/Sys.nmlZipcore/Zip.nmlStackcore/Stack.nmlArgscore/Args.nmlQ"pZ>>v$zz>>F&$>Jnz>Vf^f*$>Jn>vff*$fz>N>>j$>">b>>>>VZr 4>jbb>>$>Z>>PJ>h>.h>PT>,>>z~>>F $><>6" 0>bnn.D>>>>n&>>>Jj*6<>F*t~~>Vr~rr`RD.< x>b& Zj.*D>F*D~&$&T6F p<$2$^>>j>$v^^VVb>Jzr,v^^VVJf>J>rr>>"T~:>ZNJN>*x>"4x>\ZJNbz:>"D>lZ^bb>:$Z^2$Z^b>$Z^fn,bf&8ZZZ"T  D"$> $>V:$^>>j>$Rfb>Jzr>,^rr:$>Vb:$>V^*$>Vbr:$",bff& ZZZZ"$> " ZN^nDbv$>&$>>&$vv$vv<>Z&>$fj>ph>TV*$6$>>z   $,&4>6<*tn>>>b***$&,&&4>>6<*L2ph>>&$Z^VVb>r>Fr>>>>>>>>>>>>>>>>D6hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hd>R>F>Fr>>Vb>>~z>hT6h:h46h$6hh@>@@f>,ZvV>Jbr>>r>$ZvV>Jbr>>r>L$* "PNZ>$NN>6,>ZH> H<>Z&<>Z&<>Z>><>Z(>&(<>npp`"<>^&j>&`>Z&z>>.L>Z0>:0<>Zh>>h<>Z&>>&<>Z&>><>Z&>:<>ZH>:H<*$>Z>><>Z>>D>Z>.N><2$ZN>^&x>x<>Z>:D>Z>:L>Z&8>>8(N:Z>>>d>Zv>>Z">>Df.Zf*"JNVN^>V^^b4><$6,JVn>F>Rb:,&$:,ZN^b:4ZN^v:4NNV^j6F:4>4NVV^^>4NNV^z6N>.4>4NVN^>^N&"X >>>>*<>n>r>>F>rzz:ܞN>V^NbN^>J^^b*$ZNNrZ^^^>>Fb$Z^RNb^>J^^b"$nRfbbZZNR>FbN^>z^NfJR>FbbN^Z~>>N^N>Fj$~jR>>D $$:$2$NN$Zv$Zv$ $>V$$$2$$2$>$>$&$:$RZZ.,^RNjr>V^zZ.,fv^>fZ^f6<X > >>,>J>N>N>N>N>N>x44bR^^$ $$$$$ $44 $, $ $$ $ $ $ $ $&$ D>>>>>>>>>>>>>6 ~$$ t $ $dd d dt 4 ldD 4>>>>>>>> >>>>:44Z>< < <>>>.<><>>L><><><><:<6<$>>*<><>\>L:D6D d> <>>>>>>>>>>>*  "($&$$z~~~~>$ $ $ ,JN>>> >>>>:dJ4 $ $ 4 4< $&d66x*l*tz$>>>>>>>>> >>>>>>>>>4<>J>N>N><,jnnnnnn:D&, >L&, rvbb"< D $ $ 4 l tD4<T \ L $ $, 4 \<4D< $ D>>>>>>>>>>>>>>>>>>>>>>"8J N"$V>^&$>Z>$V~>*\>dVZj&Vz>^j2>Z2>~Z>2 VZjfZ6@Vn>>>>"P$ff> 4fZ:PV>PVj>F>PXVjZ>Pf>"D Lf.P>>fZ>P"frjZ> P28f^>n>^Pj>P&P2fv~>Pf>nj>P2R>zv~v>>&P>.P f:P>8.PHf2PpVj^2P&&P&f&>>|Rf>PP> P2`f^j^> 4 <>ZjfZ>Df>P Z>>F> (&,~jfrf*(&db>Jz&(nZj&>> 4&T.|r^f^*>!"# >>>>>>>>>>>>>"d>>>>>>>>>>>>:>>>>>>>>>>>>>>>>>>>>>>><>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>2,>$>$>.$> $> $> $> $>\V>^> &l>R>*X`>>>`N>`>`>`$>2`,>"`<,2`2l>>`2t>>>>>`2|>>`>`2>>`>2>L`>2>D.`>"`>4`2>>>>>.`>>>Lr>PV>>> XN6X*X2X$>X,> X4> X<> X2D>>>>XL> XT&X\> X>2d&LX>2l&D&Xt> X|> 4 X2>>>>2X>nRRR(,&(4(<> (D"(L(t>>RRRRRRRRRRRRR>>jRRRRRRRRRR(d&(l&(t(|2("(%"'>"$N,J~>D $J^NhNbNN@8$$Z^ff^^^"fzzVf>fVZ>nvvrr$Z^rV:,Fbvvf2>"<:>Fn^^^^z>RR^^V>N>vf>^vfvVb>VX^bb"P,>> d>Fj:>>,br>\>lZ>$>>>>^>JX>)"+h @>>>>>>>>>>>:$~ >>>>$^>>Z>>^.>*T&>Z>> h>>>hXN>4>v2>.<>&$>DJ &<>n>&$:,>>$^>^D^j>vl^j>v>"^>>>F&>*R>>"D>>>>>>>*>>R>b> @>>^>X>>4>"$>.>D~ &>"L>^f>.dF:(>"$Nrv>R(:d\>fJ:>"$Nrvz^r>F>>J>>2$>>>>0R8:Z^^VVVV:>> R>Jr>J<>V*DjRb>Z>j*4 <,jRn>JZ>f:DzH.4>^>$>>>J> $2$fn>>>:<( <( <( <( <( <( <( D( l>J:  <>J:  <>J: & fN^>f>^>. ~r>  D>V>f>.X ^>>X p RVbbrr>>~6 6 6 , >>n^r>v>"  >>  >*  -"/$"$$vn$:$:$:$>$*> $ $ $ 4 $ $ $ $>>>>-+>>>>>>>>>>>>>>>>>4>>>>>>\N>$> |l&$&4 D><:$>. d>,*, $|>6<:<6< <>>>>>>>>>>>>>>)'>>>>>>>>>>:<4>NL $ $ d L $ L"d,*8 T>>>>>>>:%#>>>>>>>>>>>>>d>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>,>>>>>>>>>>>>>>>>>>>$ $ $ $ $ $ $ $ t >>>>>>>&!>>>>>4 D ,, D>\$>>* >2|>>>>621"3 >>><>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>t>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>$>z*> D*$f$4F XT>">>>v`>``T.D &4$(>>* :<^&X>><^>...4>Z&00>$f>N>R>>j>60JNDZ>2$>ZR`>>>>>rV>R>>"$Nr>j>&TR>R> >"$Nr>F >&>>*>>>>> >~>&2 >f >> >>> ,jR>: >z: > 6 >>V>6X ^NJ>h >", z>>FRr> $>6 ,>> 4>. <> D>>: 2L>>> \ h :>Z>( ^N>v8 >~8 $>"8 ,>"8 4>8 <> 8 D>>"8 L>&8 >>>>>>>>>>> >>,>>>>>>>>>d,>>>>>>>>>d>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>4 $> D$ , $ DNn>& T> D<<&D<<<<> |< L < <>>. >D> >>>>>>>>>>>>>>>*15"7 >>>>>>>>>>>*`>>$ $>>>>>>>>>>>>Nh""2.N""".>>>>.">">$&$f>>V>J>8>nz>J86D^> ,>Fv>Z2<>>Zz>L>>" Zz>L>>`Zz>4>"$JJ>>"$bzr>dNRVVf^^>$.xVn*$.>FnZZ*$~>~>$>>,V>z^z8>$8(R< 0LZz>>j>F> r>,~>T~>H ~>0 J>^8 >8 $>J>n( $( $( 4(  V>TVv>Fv>>>> > R>Z* >,>>>>: b>F $> $~>>6 Z>>>>@ @ $~>>>@ @ $~>& >t>R:40  >>> r>^>Fv>>>jv>F>^V^v>Fv:> N:TZ>F>Fv>F>". > >>>>"8 >F>"x r > R>>v>>fVN 4>>>> ^>>>>> ~>>>~v>, 4>Z > $> >nrrr2 ,> 4>> <>> L>>>>r> > > $: ,: 4: <: D: L: &\>  FvfJ> >Vv>V 6D>b>>: >"~> > H>>>JvfJ>p>Vv>Vp6D2X>"|~"X2H`H> rjH>>>rjH>"rjvj H&p:H>n>>*>$> ,>2.d>n>>*>$> ,>j~2(.|>>>>>>>>b*p*p*p$&p,&p4&p<&pD&pL&pT&p\&pd&pl&pt&p|&p&p&p&p>>>>>>>>nv.(4rjz(\rjz(fVnf>Zjv>>Nf~> (fVnf>Zjv>>Nf~> (`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(`jfjv>>N.(>>>>v*>>Dr*>>.,r*>>\r*>>>>>*t>> >>&rjv6(r>&t>^2xdZ4J JlRh$Nr>^>Fj>>r>jv>F> $z>>>>>F>.><>zZ>~f.TRj>N:\>^>(b>>>>(tf>,r2>>>>>>v*>>:,r*>>:Dr*>>:\r*>>:tr*>>>>rjv*>>>>6rjv*>>>>>">>>>j:4>f"$6< D>>R>f^>j>2x>x$>D>&>*>>T>>~>>Vfv$$r>>> $r^>"> @@$>JR>>>>Z2>:>>>$v:>> (r2Dr2V>^2>$f>.>z2dbr>~2Tbr>~2Vjvz:4\V>vZ>&t V>ZzT$Vjjvj> >VVj>>>>&$&&4R>&dV>~v6> r>>>>>>>>>>>>>>>>f>N>>>>>>^>V>J>>z>>>>v~&6L>>>>&>>>>>>2>>>  $rj~>>h h$rR>>>*>>r>>>"rjv>6<>>@^>RvjbvZjZ:"^>RbvZv>f>2Rvjzv>"$^Zzv6.2`^>^vj>>>jbZjV>^z~Z> >2>>>>>>vV$>^20>Z0>0$R>N64`<>>R^>>V>N64<>>R^>>0r>&Hf*&`>NJ>h>>r~h6>>>>Z>>>>>fnZ>>>>(>>>>> 8Fzvzj`>N >b2>$^> L TV>8>"rj> 4 8 <>^2p >>.p $^>$ ,Vf> 8>8"~>vf6$~>4zj>"Vjj~:$"!!^^!"T 4!$Z`!JNN< !J$ZbP!>bvnZ>~RR^^Z^~:$&8",>Z^~f.L"Tf>b:>9";>>>>> \>jV8<>>2$^^j>>>>>R>>>>>f:.,6Tj>>>>>R>>>>>f:.,6T>>$>>jpn.pD>>V>>>J.D~ $r>"d HD 4>4>>n>v^f>f>>>">2`D>>n>J>F. >\*4>>n>R>>>>V  >2\hD>>n>>^ >D*,>Z>bj>N> L&,>Zp>ff>>&p`N> \"<>Z>>>&N> <6>>>*>>,, <$">D>D.T < < $ * l l >>>>>>>*972="?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> L*4$>>>>>>>>>.h: h>  h h> h">>>>:h:h  >>>$ZJn>>>>":.$ZJn>>>>":.$ZJn>>>>":L|v>2Z^:d lR>V`$>J:>4bf>Nr^>V>n^>$VbZZV>J>zb^z>zz6$N^r^V>N>^~~VV>"|> *T>f>>>>2>>$>> ,>64>TbV>>>>>>>>>RRRR@ ,.@ 4>@ <.@ D.@ L>F@ \>@ d>@ l.@ t.@ |.@ >F@ nR@ .@ .@ .@ >>@ >>@ >>@ >>Z@ .@ .@ nRRRRRRRRRRRRRRRRRRRRRRRR@  .@  nRRRR@ @ >^*@ X J^>F>Z>vz>N>N6Vj>N~~f:L T $> 0 & ^^f.  >L"|"4ZbnVb>Z>RZrnZjVb>jr>Fn>F>Vbb>r>V>H R^Vb>z>NbVV>@ >> V*d>2X$^>X<^>XT.Xdv*X|.XXZb^Jn>0>",r20>HfzZ>>>>>b>p>p>p.$>p,&p*L>J>:Fnzbbb>>>Z>>.V~VV^fb>Z.>>>$>,&"<>>>>>>>>>N   $,4&<DL>>>>T\&d>>>>lt|>>>>>>>>>>>> (08@HPX`hpx> >N>j>F>R","DhL&, 4.>>> XV>n:`>n>:`,> `46`Drr>>`d6` X*>>>>>^>>>>>>>>>V& & & .$" ., .4" .< .D" .L" .T" .\ .d" .l" .t" .|" .6 .6 .6 .6 ." ." ." ." ."  2>.V>N>N>N>brf> PnrNV> > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>L>N$ " : t> >>>>>>=7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>TDT>$ $ $ $ <&D*D*D*$ l \ , <, $ >" *@>>>2>d>|>>>X>2>>>>>>" >>>>>>>>>>>>>> 52A"C>>>>>>>>>>>>>>>> ԮV^^^^^^^^VNfffffff><>nP*P*P$&P,&P4&P<&PD&PL&Pt$*$Z6$*46,FVV^b>,$2$2$2$$:$Z> $>> >>>>>>>>>lz~~~~~~~~ t $$, $$ l $$$$ $$,>>>>>>>> AE"G >>>|Nr:$Nr:$>$> $> $> \>Z2`>>`H>VR><.H&\&H>Z2>>>Z>>>>V>>>>R~>V>ZR><.&\&>6$p$p^p$p^p,pTb>>>$>:$z>2<>f PPP$PL>>>>>6$>>&$>>&$>>&$>2,T>F>:DF>>"$>^~>6h>r&h>>h>hL> h$:h4:h,>hD>>>h>"T>h\>h.d:h>>>>>"T>h\>h>>l> h> |>>>>>>hV>>V>>>F>&$&"4>>&`F>`>VRf>>"l.>f^>>.D^rz>>>>|N>>b>>>Z>f>>>>>fZr.8>28,VV>>f>>>>>>*2$ >>Z>>>:r>>>2(>>&>>8 >>>>22>>>2H r$ ` >>>~>>X >x>~>"D L>>.2rV>jz^>>>>> nzz>>8RbZv^z>>>>`:  >$ <~~RN>>>F>>>F>>>>>>$^b>>N>>>J>>>dn> >>b> ,z>>>Z>>>^>  ^>Z>.d>>b>&$>>rJ>r>>*  $V>F>&$>>> >Z2  6 ` >2D>>>` >  $>nV>r>>>> 2` T> 2` *D*` 6\N:<>:` p >>>>f>>>>>>>h >2$>>>>>>>>r   ,>vf.  ,>vf.  ,>vf.  ,>vf.  ,>vf.  ,>vf.  L>2 T>2 d>n >>>>>: .,>>>*  >R 6 >>>$>>>>br>^6 ><&  >>>r>>>>>>>>* >>>>>$>>>>br>^6 ><&  >*>>r>>>>>>>>* >>>>>"$>>>>br>^6 ><&  >*>>r>>>>>>>>* >>>>6 >Fr>>>>^> `N>>>n>h>h>h$>*h,>>h<>>>>&h>>L>F>>h>"dR>>>$ >h2>>>>>>h> ^>>RV>>>>>>>h2>>h>>h>&h>hh>J>>h>Z2>>>>>*h>>^>2>&>>>$>>,&h2>>*h8^>>>z>>>>>b>>F>>b>>>>^>>h>6h>j>>>N>>b>>>>h>>h x",>>>>>$>F>>&<&\N>bj>z>>r>>>>>R>Z>6\&>T 6\>| <>Z>>>">>>>>>>>">,>>>V>r>R>>\f^.f>>>>>J>>>>6D>Z2>>>& ,>(JP8v>$ ,>j: $R> <D>>>"I"K&>>>>>>>>>>>>L>>z>>*>*>>$",>4\:$> $>$>>$$"$.$>$fJ>>:>J>V>>Z>:J>>>N>2\>P$>6PT>Z>:J>R>>>"$>N>>d>$>6T>Z>:HJ>>:`>`>H8N>>\>$>6T>Z> J>>>N>&\>>J^>>$>h^hh$hLfN>6$>&D>L6T>>\>>vrf>>> $>r>&>"<>44.t>0,>@>&04>0<60D>>0L>0>d>,>~N>b>>> >>:$>>,>&>"4>&>>><~J>6>66>.\bJ> >6 6>:|bJ>0 >60 6>>>>.>JJ>f>P >6P 6>>>~J>6x >6x 6>">  > > >>Z>" .,>V>  >:>>>>R>J>>>n p >>2p >>>>p >$>.p 4Zz>V>b` .t&` . DF: >"$>bfv > >* >"$> >4>>&N>>T6 ` >v>.` ,>*` >.` >.` $>*` <>^>R>>` \>r>>b 6 >* ,>>` >` >>>>"D>>>>n X > X >X >2$X L>&. >F>>>>>>>>>>>4Z>>>f&,"$>^2 >$> ,"$>^2 >L>.$>^2 >>^*p >^*p >^>^>^.  4Z>>>f&,"$>^2 >$> ,"$>^2 >L>.$>^2 >>^*p >^*p >^>^>^.  4Z>>>f&,"$>^2 >$> ,"$>^2 >L>.$>^2 >>^*p >^*p >^>^>^.  4Z>>>f&,"$>^2 >$> ,"$>^2 >L>.$>^2 >>^*p >^*p >^>^>^.  4Z>>>f&,"$>^2 >$> ,"$>^2 >L>.$>^2 >>^*p >^*p >^>^>^.   >Z>^  >Z>^  >Z>^  >Z>^  >Z>^  >Z>^ >Z>^ >Z>^ X>^ X>^ X>^ X>^ X>^ X>^ X>^ X>^ p>>bp:,>>>>>>V>>v>b&&4&p.t>J>^>^&p >J>^>^>~>>: @> h>>V>>>N>^"p4>J>^"pT>Zpl>>J$>Zp&p>>R:@>>r>>F:@4>@D>J>>^V$>@Hj>j>>>">nn>>  $>V^V>j>>n>J 0> H$:0 L>^*x>&x $>^>>v>J>>,RRVb^rVZ>>>>>>>>>>>>>>>>ttttttttttt$>T>d>$>4>D>$>:$4>V>r >>> V>X>>VbVbz>Rrj^>f>F>>J>N>Z>8N~N^>.X &|Z>br>&.h>>>>>>>>>>~>6,>>>>>> >:$>x>:|>x>>h>6>*h*>>h>>j>>:0>0h&&hlbvtj>Rn>j>$V>^>& $>Z2.x>>J>>x>4^x>&L>F>6xlV>JZr>ZN>.\>>2d>z>~>~>>>(V>>>>0>$>024^r:$>p*,Zj>>0n>J>r>"$>2&L>f>^ l>.0>>>>>>>>>>>>>>>>R>0>>>.>>n>0>>> >>>>> 0>:>>>0>>>:j>>V>0>>&8n>>z>F>nR>>p>P.b>b>J>"P&00nr>Z>N>^>> 0>>xn>Zr>Z>>0>n>Zrr>^>>:0>>>J^>020>>:0`>>0@n>b>^>0>&h> 0>"xj>&0>~>>*02>>>>>nJ>f>v>>Z:>"<>>R^*$ <2(>J>>f.>&>J`>`$>,4>`d><D>` >>j>Nz>60@>>>~>^>608>>>j>j>^H*H $0D>.t> 4 <>"0pn>~>~>f>>0n>z>F>60n><><>>0>:0n>Zr>Z>20>Z*>6 $r4>X^>>^j> D>j*>"> $>" 4>V>>`>>>>`>Z*`>n>FJ:`>fv>J`:p.XV>>^J>rh>"$> h64>V2 : >>>Z>Z2 >>4fj>N>6 >"\f~>v>V>2 @V>"d>6>>T >v$,$>>>2d>>Z6> |>>>N>.>>>f>f>>>"T XH&t&H >&2>Z&>>^>>"4>r>>n>> > >>> 2,Zz~>>>V>>>>>`>>`>>>>>.`",>>>>Z~>6 .>>Vj>Z>>>Z>>J>>Jb>>  f>" >>"!b>>fVZvv>>F> jR^f>> l !t^>N>b!j>N:! Zj>>>>>v~N>* "&$& "!> d!.t! b>>" !:~6D>>"Z>>>>>^V"Ξ">4>J>>0#>>0#: #"DF^>`#`#> ##&#>>|.%Zn>&#F>#>"$>RJ>>#>f"#><LJ:H$ 8$ު$Z$$Fp$ $RV$~r>N^~>>n>%*$Z>"$> T&$ dzb>J#:@&>>>.% %J% %\6$&&%F%%,%">>>V>>Fn>>*p&>>$>>z>j>M"O>>>>>>>>>>>>>>>\>>>>>>>>>>>>|>>>>>>>>>>>>>>>>>>>>>>>>*>>>>>>>>.<>>>>>>> <>>>>>>D>>>>>>>>>>>>>>>>>>>>>>2\>>>>>>.4>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>v*$> 6,> 4<D>6lf><>>JRRRRRRRRRRR >>>NRRRRRRRRRRRN&&2">>>>>>>>>>>\>>>>>>>>>>>>|>>>>>>>>>>>>>>>>>>>>>>>><>>>><>>>4 >>>>>>>>>>>\>>>4>>>>>>>>>>>>>>>>>>>> >l < >>>MKQ"S >>>&TJ.4zN~*$&$&$>>V@>>@*@D&$>^>*>.<>^>*>.\>T>v>*>$>22,>>>>&4><>D6l>fxxxD>j.$.4.,<&d>>f  > $L~` ` `z$>Z2>"<>DZ>j>.X>Z2XR>2D>>X>>>^2>j>2LFnz" >"4>& :\>*4"$>Z>F6>>>>>4v>> H>>>$>>>H>>>>H>"L> H >2 R>:l> >>>>$&>>>>"T>N>>&>>>>N>>>>6>N>>>">>>&>N>>>> >>>>l>Nb>>>>>>>6D>>>>>>4>>>>>>>>>>>.>N>>>>>>>>>>>>>>>N>>F>>>>>>>>>&>>>>>>> >N>>>>>>>>>>> >N>>6>:0 >>2R>>>&H >p >2p >$>R>V>>>&p \>* >>$b>f>* >6T& >>6\^z>R>V>F>>>> >>>h >:$>J>V>>h >2D>J>V>>N>>>>:h .|&h  & & @ >( jb>>>R>( *$ ( &,>J>N>^` >2` >` $>>>>>>>>>>>>>>>>>>>>>>> >>>4R$L $ $> D $>> <><:l6D d L D> D $ < $ L\ $"$ \*>>h >2\>>> >>>>>>>>>>>>>>>>QK>U"W` >>>>:l>Z@@<>>&$^^j>>>J>n>>>>v:&,6\j>>>J>n>>>>v:0&,6d>>,>Z2>>>:D>Z&>>>D>N>>"D>>X6$j^R^>>>X|~66>>>>&$>>j>>n6>2<&d>N0>0&$>*0L>.x6$j^n^>n>>n>>x6(D `D 4jD>Zr>>N>D*,>Zhr>R>>>:hXN>2\"D>N>>>>>6$Z^>j>>b>j>>>2& >22D>N > >>"$>>>>> $>>.4&>l& >22pD>N >>>"$>>>>> $>>.4&>l& >228 <>Zp bZ> p ` N> L&>>6>>,< <$">*D6D2D>*|>"d6L>*:< < < < d*666>>>>>>>>>"UKY"[>>>>> ,>>n:.L:$>>>>>>>>>>>>>>>F pV pV pV pV pV pV pV p p p p p p p p p p p p p$p$p,p,p4p4p4p<p<p<p<pDplZ^ZNZ$^>br~>>8>,>>8T^>b>>">"<>Z>^>>v>> T>Z>^>>>>&L>>^`V`V`V``<:$$j2>>V2>>R6>>>2>>>>> >D>>>N2>>>>>N>(>>>*(>>>>>>>>>>4.>>4.>>4.>><>>>>>>>>>D.>>>>>>D.>>L>>>>b2`>>R>>&`>>`:>>>>>>l.>>l.>>>>>>l.>>l.>>l.>>t>>&>>>>>> |.>>>>>>>|:>>|V>:>>>>>>>>>>2.>>>&>>>>>>.>>>>>.>>>>>J>.>>>*>>>>>>&.>>>>>>>>.>>>>>J>>:>>>*>>>>>>>".>>>>>.>>>>>J>>:>>>*>>2(.>>0>X>>>>J2X>>>>>N>6h>>>*h>X>>>><~r.>>N>>X>>>>>.\> X>>d&X>>>>&t>X>>>|.X>>>>:>*X>> Z>>f>>2>>>*>>> >>>>Z2>>RZ>>>>>>>2>>$>>>J>> >>>* >>>>"L>>>>>^>>H>>>.H>>>>>N>>`>>>*`>H>>>>>*>>2>*>>N>&D>>>>N2>>RZ>>>>.(>>.(>>>.(6$&(>D>>l>>>>>Z>>>>>>> >>>><>>>>>>>>>b.>>Z2>>R>">>:>>2>>>>> 4.>><>d>>>>>>>>r.>>Z2>>R>>>>>2>> >D>>>>R X>>>>>>>2X>>2X>>>X>>2>V2>>R6>>>2>> >D>>>>>Z>>>>>2>>>>>> >D>>>N. >>R6 >>>>2 >>>* >>>6 >D>>>>Z2P >>R2P > P ><>>>>>j > ><>>>>>Z >>V2 >>R6 ><>>>N.0 >>R0 0 ><>>>>>>&h >>>>>>>2h >>>>>>>$>>h >>>>>>>>>*4>h >>>>><.h >><h <:h >><h >>&D>:h >>:lj. >>N F> >>R>. >64>>>N: >>R  ><>>>N.8 >>RZ> 8 >>>>>6$>68 >>>>>>>>,.8 >>>>>>>4>>*8 >>.D8 >l>>>>>>>b2 >>R6 > ><>>>>>>>^6 >>>>>2 >>>> > >D>>>>>>>f2 >>R6 >>*4>>>N.H >>RH H ><>>>r: >>N> 2 >>> > >>>>4* >>4> ><& >d>>>F& >>>>>: >> >>>: >> >>>>>>>>>$* >>$ $6 >>$V>6 >>>&T>>>F&0 >>>20 >>0 >>>>>.0 >>20 >>>>0 >>D>>>N2p >>Rp 2p >>p >>V2 >>Z2 >>>>>F. >>Z. >>>>>>>J: >$ >L>>>N2 >>Z2 >>R6 >>NZ>>>>>>>>>>>>8>>>>>>28>>Z>>>>>&8>,*8>>4>Z2h>6h8>(>>>>>J2>>>>>>>>>>>>>>>>.>>2>>>>:$>>.>>>>>>>>,> >>,*,:>>,V>:>>>>>>2D2>>>>T2>>>>>>>>6d>>>>>>&l.>>>>:t>>>>:|.>>>>:>&>> >>>>>>>f>X>>R>>X>>*X><>>>>>r2>>V.>>R>>">>:> 4*>>4.>>4> <>d>>>>>>>f2>>R6>>>>> >.<>>>N.(>>R( (>>V2`>>R6` `><>>>N.>>R >>V2>>R6 ><>>>N.>>R ><>>>>>>>>r.@>>Z2@>>R>@>>>>2@>>@ @>Dj.>>N>><>>>>^.>>R >Ln>>&$Z&>TFv>H>"$>.H>]"_>>>>>>>D>>b>NR $r T>,>4.$Z>>>>>$>:$>&$>t.$.$jnnnn~~nnnnn2,>R>^^Z>>>>Vb>>^^Z>>>>Vb>>Z>Z>Z>Z>Z>Z>>>N^>Z>^^>j>>nn.L>>r2>>"a"c(>>>>>>>> L>>j>>(>NRR(4r(d>,>4.$>:$>&$Z>>>>>d.$.$>nnnnnnnnnn24Z>^>>F>F>F>F^^Z>>>>Vb>>^^Z>>>>Vb>^^>j>>>V>V>F>>r2>DJ>n>n&$vzzzz>b>V>NnN>nv>r>n&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>L>d ,Z^>>>>>>>>>.$$$*,Jfff$$$>N>F>F>F>F>F>F>F>F>F>F>Fnnnn>,~>F>F>F>F>D>D>4>b>f>F>>>>>>>>>>.Jn>F>>R4J>$>$>$>$>$>4~n>>>>R((Jnn>>R(>>>>>>>:a_>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>D>T ,Z^>>>>>>>>>$",$$,JNNNZ$ $J>F>F>F>F>F>V>F>F>F>F>F>F>Fnn>b~>F>F>D>D~>F>F>F>F>F>F>4>4~>F>F>Rnnnnnnn>>>>>>>>>>>>>>>R.>>>>>>>>][>>>>>>>>>>>>>>>>>>>>>>>>>>>>>,><$ l\>>"\>D:T6L <$ $>>>>>>>>>>>>>D>>>6P>>6>>*>l><>*d>D><>D>D>D><.<.<><><>*l>4><>*l><>D>4><>*d>T>D><>>L><>6>>6><>*d><><><><><><>D:<:<>>>>>>>>>>>>>>>>>>>>>>>>>>>"YK>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> \t>>>>>>L>>\$ $ $$ $ $ $ $*|&**>L6&L< t>> t>> >"X >>>>>< L2`*>"20>>>>>>>P>>>>: >>" l>%&&>>>>>>>>>>>>>>>>>>> IG>6e"g>>>>>>4>>R:&>D:$>>>>>>>>>>>>>>>~ xV xV xV xV xV xV xV xV xV xV xV x x x x x$x$x$x$x$x$x,x,x4x4x<x<x<xDxDxDxDxLxt$^>b>N~>>&>,>>"\j2h>>V2h>>R6h>>>2h>>h>>> h>>>>>b2>>R>>:>>>>>*>>>>>>>>>>>Z>">:>>>>&<>>>>D.>>L>>>J>">>>>>>>>l.>>t>>>J>6>>>>>>>>>*:>>V>6>>>>>.>>.>>>>">>>>>.>>>>>>.>>>>>>>>>>.>>>>>.>>>>&>>>>>>>>.>>.>>N>><>>>>>>>.>>>>>R2>>R>>>>>>>>>>>>n2>>N>>>>>>>>>>*n2>>N>>>>>>>>>>*@:>>>>2H.>>>>>>>>>>2H.>>P>>">>>>>>>X.>>`>>>>6>>>>>>>>>>>>>2>>$>>>J>>>>>>>>>>D*>>L>>>J>>>>>>>>>>>l*>>t26>>>>>>>>>V.@>>Z2@>>R>"@>>>>2@>>>>@>>:@>>2@>>>@>0> <.0>>D0>lj.>>V2>>R6>>>2>> >D>>>>>>>b.>>Z2>>R>>>>>2>> >Dj.>>>>~.>>Z2>>R>6>>>>>>>.>>2>>Z>^z>&0>:<>d>>>>>b2x>>R6x>>>2x>>x x>Dj.>>V2>>R6 ><>>>N.>>R ><>>>>>>>>>b.8>>Z>8>>R>>8>>8>>>68>(>6T>>>N:x>>Rxx>Ln>>&$Z&>TFv> >"$>.>LZr>^*i"k .,^^bnj>&$Zb>> ,>>R   $,.4.<*D*L*T*\.>>>>>V H H H$H,H4H<HDHLHTH\HdHlHtH|HHHHHHHHHHHHHHHHHHHPHX.HZ>4^b*^.$ ,b> $r:^b*8^^>Vf>Jx>>>>2x$>&x4>&xD2xTb>xlb>:xb> x6,>&xbf6xbf6xbf>xbb>x>R> x^ff>xx> x(x8> xHxXbf*xx>"x>"x.x>x>x>J> xb>:x.x&8Z 0$>^*>>>>>>>>>>>>>>>>>>>: 4\> L x>>,>\6<>>" >>>>>2ig"m"o>Jn>>N*>2> $>n20>> 0"tpRh&>":"$>>>>>>$H>>>F>>>>:d6>6T>>>>N>&> $>D>^>.\>^>n>D>>>>>>>>>>>>>F> ,j> Dj>" \j~>>b>>b222$.4> >> >* j>> >r">>> 0 X @"@* h x >>>>N>>>>>>>> j6 j6 >N>> >>>F>>>> >>>F>>>> 0 @ ` P"P* pj6 RVV>"|>^" >>  H>^& &`> Z>>f&> V>>>>>> H>>>64>>>> ^VVV>>>>20Z>0Z>0Z>TZ>lZ>Z>0Z>0Z>Z>*Z>>Zn>>Z>>> Z>>>"Z &>>@Z>>>""XZ>>>22pZ>>>>>>>&2Zr>>>*Z>V>>2Zr>>>*Z>>>>&Z>>>>&(Z>>>"@Z>2XZ>2pZ>R>>>>>>>6f>~j>N>>>v>2$*>q"s >>>>>>>><>"\>>^^JN^Z>r>RRR>>r2(RVVl*|RJj>bn24fVv>XN6X<>Vv>N6v& n&  ^, 4&T>xZ~>d>>>:,>>Nj*>>*&6>>N>trf& >>>>>>>>>>>>>><>$ :$ t < < 4<< < | D> t>>>>>>qo>>>>>>>>>>>>>>>>>>>>>>>>: >>&D> >>>mg>>>>>>>>>>>>>>>>>>>>>> >>>4>D $ t $>>>>:d>D>>*>6>*l>D>D>d>D><><> T:<*>>>>>>>>eG>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>,L 4 4 $ $ $ $ > T< $ <> L T>>L>>>>>> >>>D>*.L>6` >> >6>>>>>>:> >>>>>>>>>>>>>>>2E2u"wX8>>R88>>8D>>Rxf xx<>J>>V2T64Z>44$.$>>>>>z~~~~4 D < D4 4z:4 ,$$ $>>>>>>>u"y"{8>>>>>z~~~~~~> ,>$$&$$>*$>>:$>*$$:$:$Zf>V>R^>~>L>R>N>@^r^*8,>:,V^*>>>>> >>>>>D4 l $ $ $ $$ $$ $ $ $>T>>>>>>>y"}" >>>>>&\ZN^Z^Zf>Nv>4*$*,z2> >&4T>>>>>>>4$ $ $ T>>"}"">>>>>>4*,Z>V^ &4"HRb^>RrrxJfrr>>>>b>&$~rr2L~rr>>>J2&>"~^Z^2>>>>>>>>> >>>4>L>6>>>>>>>>>>>>>>>>>>>>>>>>>>2 D L \ d \>JN>v>FVRjV^T>n>R>f>f>R>>>>p>\>J>f>$>j*D"L>b^8Zf>^:p>>>>>2p>2p>2p>2p>2p>2p66p>>>newrethrowloaderloadprimsmakessetsgetsblitssubsfind@make@empty@consstringasizearrayamakeablitthrowasub@compare@print_unionnargsobjfieldsfieldobjgetinvalid_argintfloatprintgetposcacheNoneSomeNeko_errorInvalid_argumentAssert_failureErrorStream_errorNot_foundExit@print_record@aget@aset@pconsstream_tokenstream_posstream_junkstreamsprintfsndprintfordnstringnprintnekominmaxmagicignorefsterrorcomparechrassertsubsortsetmapmakelistlengthiteriiterinitindexcreateblitappendadd@rmaketlsplitrev_recrevphysnthnonememiter2hdfoldfindfilterexistsconcatchopassocallresetadd_subadd_charssizeuppercaseunserializeunescapeserializelowercaselist_depencyis_printableescape_charescapeOverflowEofClosedBlockedwrite_ui24write_ui16write_stringwrite_i8write_i32write_i16write_filewrite_charwrite_bytewritestdoutstdinstderrread_ui24read_ui16read_stringread_lineread_i32read_i16read_fileread_charread_byteread_bufread_allreadoutputinputflushfile_write_charfile_writefile_stdoutfile_stdinfile_stderrfile_read_charfile_readfile_outputfile_openfile_inputfile_flushfile_contentsfile_closecreate_outcreate_inclose_outclose_inhkeyhnewhcounthgethmemhaddhremovehsethiterreplaceremovehashEmptyMatchStarPlusNextChoiceInvalidRegexptransitionsstarsingleplusparseoptnodesnodenextmax_codemake_transmake_tablesis_emptyinvalidgroupescapeddeterminizecunioncintercemptycdiffccomplementcalladd_nodesadd_nodeInvalid_ruletokensourcepunionnull_poslineinc_lineemptydatacurrentcurposcharbuildTrueFalseNullThisIntFloatStringBuiltinIdentVarWhileDoIfElseFunctionReturnBreakContinueDefaultTryCatchSwitchSemicolonDotCommaArrowBraceOpenBraceCloseParentOpenParentCloseBracketOpenBracketCloseConstKeywordBinopCommentCommentLineNormalWhileDoWhileEConstEBlockEParenthesisEFieldECallEArrayEVarsEWhileEIfETryEFunctionEBinopEReturnEBreakEContinueENextEObjectELabelESwitchvar_argss_tokens_keywords_constantmk_stringmk_intmk_identmk_call1mk_call0mk_callmk_builtinmk_binopto_stringprint_listprint_astnewlinelevel_exprlevelMutableImmutableTAbstractTMonoTPolyTRecordTUnionTTupleTLinkTFunTNamedTVoidTIntTBoolTFloatTStringTCharTIdentTConstrTModuleMRootMFailureMHandleMExecuteMConstantsMFieldMTupleMTokenMRecordFieldMJunkMSwitchMBindMWhenMNextTConstTBlockTParenthesisTCallTFieldTArrayTVarTIfTFunctionTBinopTTupleDeclTTypeDeclTMutTRecordDeclTListDeclTUnopTMatchTTryTTupleGetTErrorDeclTWhiletlinkst_voidt_stringt_polymorpht_polyt_monot_intt_floatt_errort_chart_boolt_abstracts_types_mutables_funs_contextpolymorphizepoly_idmk_unionmk_tupmk_recordmk_funmkis_intgenidgeneratorfile_nameetypeduplicateabstractidivNoderemove_min_bindingmin_bindingmergeheightbalAccNullAccTrueAccFalseAccThisAccIntAccStackAccGlobalAccEnvAccFieldAccArrayAccIndexAccBuiltinSetStackSetGlobalSetEnvSetFieldSetArraySetIndexSetThisPushPopCallObjCallJumpJumpIfJumpIfNotTrapEndTrapRetMakeEnvMakeArrayBoolIsNullIsNotNullAddSubMultDivModShlShrUShrOrAndXorEqNeqGtGteLtLteNotTypeOfCompareHashNewJumpTableApplyAccStack0AccStack1AccIndex0AccIndex1PhysCompareTailCallLoopGlobalVarGlobalFunctionGlobalStringGlobalFloatGlobalDebugGlobalVersionInvalid_filewrite_debug_infostrap_stack_deltaread_debug_infosop_paraminullhash_fielddumpcode_tablesXEnvXStackXGlobalXFieldXIndexXArrayXThiswrite_optrapstack_deltaset_posscan_labelssave_breaksprocess_continuesprocess_breaksmake_arrayjmpgotoglobalget_cases_intserror_msgcompile_functioncompile_constantcompile_builtincompile_binopcompile_access_setcompile_access_getcompile_accesscompilecjmpcheck_stackcheck_breakspathVNullVIntVFloatVBoolVStringVObjectVAbstractVFunctionVArrayvalueneko_valuemodule_set_globalmodule_read_pathmodule_readmodule_namemodule_loadermodule_globals_countmodule_get_globalmodule_exportsmodule_executemodule_code_sizeloader_pathasetaget__list_depapplyCharConstrModuleTypeThenWhenExceptionQuoteVerticalStreamOpenStreamCloseETypeEPolyETupleEArrowEAbstractEAliasERecordEUnionATypedANamedATupleSPatternSExprSMagicExprPIdentPConstPTuplePRecordPConstrPAliasPTypedPStreamEVarEUnopETypeAnnotETupleDeclETypeDeclEErrorDeclERecordDeclEMatchETupleGetEApplys_pathTotalPartialDubioustotalt_conststream_patternstart_by_a_variablesplit_matchingpartialmswitchmake_token_matchmake_record_matchmake_construct_matchmake_constant_matchlines_of_matchingjunkhave_whenhandlefully_matched_reffully_matchedflattenfailureexecewhenerror_refdivide_matchingconquer_matchingconquer_divided_matchingcondbindalways_addadd_to_matchadd_to_divisionunionremove_min_eltmin_eltjoininterdiffadd_loopInvalid_characterUnterminated_stringUnclosed_commentUnclosed_nxmlInvalid_escaped_characterInvalid_escapestrnxmlmk_floatkeywordsidentexprestringenxmlecommentcommentbinopspacesnumbersnumbermodidentUnexpectedUnclosedwhen_clausevarsunion_declarationunclosedtype_path_nexttype_path_modtype_path_list_nexttype_path_listtype_pathtype_opttype_declarationtype_decl_plist_nexttype_decl_plisttype_decl_parametersstream_nextstream_liststream_ident_listrecord_fieldsrecord_declarationprogramprioritypatterns_beginpatternspattern_tuple_nextpattern_tuplepattern_recordpattern_optpattern_nextpattern_mod_pathpattern_list_nextpattern_listpattern_declpatternparameters_nextparameters_decl_nextparameters_declparametersmake_unopmake_list_patmake_listmake_binopis_unopident_optfunction_recexpr_shortexpr_nextexpr_constr2expr_constrcan_swapblock1blockCannot_unifyHave_no_fieldStackUnknown_fieldModule_not_loadedCustomNIntNFloatNStringNNanverboseunify_stackunifytype_unoptype_typetype_patterntype_matchtype_functionstype_formattype_exprtype_constanttype_blocktype_binoptype_argsave_localss_ttyperestore_localsregister_functionpropagateopen_filemodule_infosmoduleload_module_refload_modulelinkis_tupleis_recursiveis_aliasget_typeget_recordget_moduleget_identget_constrerror_msg_loopcontextaddableadd_localparse_switchparse_stringparse_opparse_listparse_from_stringparse_fieldparse_expr_optparse_exprparse_constantheaderdocstackcdatapcdataxmldonePCDataCDataDocumentnode_textnode_nameis_pcdatais_nodeis_cdatafirstNodeattrib__listto_xml_recto_xmlparse_xmlparse_posInvalid_nxmlvariables_nextvariablesswitch_casesparameter_namesobject_fieldsNativeStructuralrecord_indexno_labelis_funimportgenerategen_variablegen_type_printergen_typegen_matchinggen_match_recgen_matchgen_labelgen_functionsgen_exprgen_constructorgen_constantgen_callgen_blockgen_binopenullcoreconstruct_idcomparisonbuiltinarityargsversionwithout_extensionwithout_dirset_cwdread_directoryput_envis_directoryget_envget_cwdextensionexitexecutable_patharray_dependencyNOSYNCFULLFINISHBLOCKuncompressoutput_set_flush_modeoutput_endoutput_bufferoutput_boundinput_set_flush_modeinput_endinput_buffercompress__resultacopycallstackexcstackCFunctionPosexcVoidInvalidparse_argshelpFileNotFoundreportgen_nekocompletecapitalizeL/`S6_L5LL!fLL%LXVL\fLL%LXVL/f Ll^L`L4VL!f7LLVL.V7LnL-LZV^!7LoL)LOLLLczLZVL+rLGLLL)QL)LqLPVLgf rLL:VLLLLL3VLLLDVLL/LL-LAVL6aRrLGLLL)QL)LtLPVLgf uLL:V!LLLLLL3VL8VLLLDVLL)L2VL6YRrL!LXVLLabLzf^?L!L-L5LQVLL!L-LaLaLaLALaL9L[V襥xLLM9LL_ $zgfLL5L-L:V9L)L)L^+rj LLl^(LLf LBaLzLC^L/*LLIL?L>L9LLLJ*R9L@L)L=L9L%zgL9L9LL\9L9L@LLEL%zLKV=LNLLL!b L%b#^pgfLL9L0VLL:2^RQL)ULjL L=l^3LLf LBaL_ $fL)LV^ ^L/*A^r9L@L)L9L%z9f)L)L)L=3^)L)L)L3rgf uLL:VLhLlL-VL9LLkVLL%fQL*fUL^^^^^L襥L=LL`LFVL=!R9L}L-L0VL)LLL^+riL/`S7_LL9LLt! zLLLLLLL)LL/HoL#g%Zrrj9LLLL=Vl^$LLf LaL^L/*)r9L=j 9L=l^$LLf LaL^L/*L9LvLL=LvL)LLvzLf^L9VLL=!r9L=f LL=L9LVL-L-LL9LLV9L9L)r9L=f LL9LVL9L9L%!LLLLLLvLL-L-LLvzR9L=9L=L9LL=LvL)LLvL-LzLf^L9VLL=!r9L-L-L-LVr9LLLLLLL-LzLLLRLLzrLLL)LL)L!bL!b)Lb )L)Lf L-L-L-LCrL!f LL!f^YLL!L)LL!f<)L)L)LVLL!f L)L-L)L)L)_)RrL!f LL!f^LL!L)LjXL!fJLLL VLL!fL!f^LLL)LL)_l^FLLf LaLf$)L!f L-L!LLV^ ^L/*9RrLLLj"fLLL9VLV_Ll^/LLf LaLf )L^ ^L/*)RLLj5f%LLL f LLLV_LLVl^.LLf LaLUDf L^ ^L/*!L9LL9L)L9L-L9L-L-LL)LLLRL9LL9LLLRL9LL9L)L9L)L)LLLRL9LL9LLLLLL!f L^RLL!rLLQLLLF)LL-LzrL)rLLL)rLbLf LLLL9"r)LL)L!bL!b)Lb )L)Lf L-L-L-LCr!LLLL!f<)L)L)LVLL!f L)L-L)L)L)_RrLL9VLLL9VLLL9VLLL9"rL!bLf LLL9VLLL9"rLbLf LL!fLL)L9"^ LL9"rL!bLf  LLL9VLLL9VLLL9"rLULLL!rLLQL FLLLF)L)L)LVL9*r6L/`S7F_ L/jL!LLfLLL/%[VL%a_RrLL/ RVLLf &L r)L)L)L/%[Vf (L !rjLLLLL/(-Vl^Lf *L 1r)L)L)L/2[VLLf ,L r)L)L)L/,VLLf !LrLL#VLLL#.Z!rrLLL!b L%b^P^MUL!fQL9LLVR^4^^QL)UL9L)LV9L=LVL1^LLLL)LL-L0va)LL)RrL fL~LLL b$L b%L b&L\b'L"b(^.^*3^X^"4^P^5^H^6^@^ 7^8^LL9f8LL"LV^^^L%LL=*L9L=f L^9LLVLLL b$L b-L b6L\b?L"bH^V^RL3LV^^BL4LV^q^2L5LV^a^"L6LV^Q^L7LV^A^LLfL8L)L"LVLV^^^LLLVL%LLLLL-L9LL=LLL:va!L!RL9LL=L!L!LLg< L)L/ RVLL\g)L-L%)Lf <L L-L/ RVLLu^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^_]____Q____E^v^t^r^p^n^l^j^h^f^d^b^`^^^\^Z^X^V^T^R^P^N^L^J^H^F^D^B^@^>^<^:^8^6^4_^0^.^,^*^(^&^$^"^ ^^^^^^^^_^ ^ ^_^___LL"L/%[V__LLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR_ _LLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR_K_BLLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR__LLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR__LLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR__LLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR_O_FLLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR__LLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR__LLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR__ LLf <L LL%L/ RVLLLL/ RVLLLL0bL9bL0bL9f <L 0L"LL"LLdL-L"L)L L)L"L)LLf <L LL)LL/%[VR^S^JLL\L/%[V^A^8LL L/%[V^/^&LL L/%[V^^LL L/%[V^ ^<L ^^^LL-L)L/%[VLL%L)L%_)L!L)LCRL9LL!L)L=VL!LaL"LAL"LLfHL)L/ RVLLAfLZf-L-L)L"LLL/%[V)L-L%_)RL9LL!L)L=VL!LAL"LaL"LLfHL)L/ RVLLafLzf-L-L)L"LLL/%[V)L-L%_)RL@L/HoLBF7L/`S7|_fJ#MRLLVLJL#1iZfrL)L)LJL#1iZLX"rrLJ#MRLXLLL!b L%b ^!^UL%LL9𭈥^LLL!b L%b^\LT^ QL^LLL!b L%b^^LT!^ UL^LLL!b L%b ^0R^,QL)ULKL)LLL-L9VL%z^rLLL!b L%b^$^!QL)ULLLL9B^rLLL!b L%b"^y)LL!f^ ^^bLT9^\QL)ULLLL!b L%b^2bLTQ^)QL)ULL)L VLL)L9s^^rLLL!b L%b^PRLR^GQL)QL-ULL9LLLKLL)L%zLKLLL%zR^LLL!b L%b^)^&QL)ULLf^ LL9B^rLLL!b L%b^0^-QL)ULLLOVL!f^ LL9B^rLLL!b L%b^@ULQ^7QL)QL-ULL-LOVL!f^ LL9^rLLL!b L%b^<ULQ^3QL)QL-ULL-L!f^ LL9^rLLL!b L%b^/ULQ^&QL)ULLf^ LL9^rLLL!b L%b ^*)^&QL)ULLKL-LL%zL9B^rLRL9LLL!b L%b ^+-^'QL)ULLL-LL)L9^rLLL!b L%b ^+)^'QL)ULKL)L)LL9VL%z^rLLL!b L%b ^')^#QL)ULLL-L9VL=B^rLL9L)LnvaLRL)"L!f^;LLL!b L%b^!pLT)^ULL%LL9:^rLLL!b L%b^)^&QL)ULLfLL9B^^rLLL!b L%b^)^&QL)ULLf^ LL9B^rLLL!b L%b^3tLT^*QL)ULL!f^LL%L9^rLLLuaL!f vLT)L)L)rLLL!b L%b ^?R^;QL)ULLfKL)LL-L9VL%z^ LL9B^rLLL!b L%b^/^,QL)UL9LL-LMVL%LL=B^rLLL!b L%b ^OLP!^GQL)UL%LL9L)LLVLLL)L)Lyva%L-L)VR^L9L)LLSVLN!r|7GL/`S7_LL)QL!LL)f.-L-L)$L~L#(02L%aL)f -L2_-L2-R9LL/1&L)zQ9L!L/m%zL)QLL/1&Lf,LL%L/~4"eL!LL!LL/}V-L)L)L@-LL%FRrLL"r)L)L)L3rL/~4"L!LLfL)L@L%a_9LL-zRrL/~4"L!LLfL)L)L@L%a_9LL-zRrQL!L)2L)f)L$LL%a_RrQL!L)2L)fL-L)$LVL%a_RrQL!LL/~4"L-2L-fLLL$L@L%e_9L-L)zRrLQL-L+rQL~#MRL)2L!fL%e)L)$LL~L#1iZa_RQL!L-2L)f)LL)$L9VL%a_Rr)QL)L)Lf L/&]9L)LLLL/[uVzrLLLLL/}UrQL)2!LL)f$-L$L-L~L#eZL!frL%a_RLr7}L/`S7_LLL!b L%b^ L-LL#KZ^L-LL#KZ^9LL%zLLL#KZ9LL9e*zLLL#KZ9LL% zLLL#KZ9L)L)LvzrrLLL#KZ9LL5/zLLL#KZLLL#KZLLL#KZLflLLf L/\L%f LrL)L/1&L!LL)f+)LL)$L2L%aL)f )L2_)L2)rRL/(0 L/mLL!L)L/1&L%LLfL-L)$LL/LLf )L%2^!LLL L-L/AVLL2-L%2R_)L2)RL/1&LLf)rL-L2L)f+-L-L)$L2L%aL)f -L2_-L2-rRr)L)L!f!r)Lf)L/1&L)L/1&LLfLr)Lf!LfLf LrQa_!LLf4L$LL)$LLLzLVLL!frL%e_!rR)LfLf!LfL-f -L-rQa_-L/mL!LL/1&LLfHL-L-$L/AVLLL$L/AVLLLL zLVLL!fr -L%2R_!rR)L)rrLLL+rL!b L)Qf LL#eK3L7L?O17LL7L?7.LL7L?%LL7L?x+LL7L?;WLL657L?LLw 7L?LLUD7L?>#-LL/HoL#g%Z77L?pL?K7L?eL?SL?!nL!777L?!|L?1iL?MRLLLLLL)LLLLLLLLLLLLLLLLLLLLLLLvLLLLLLLL?&]L ?(0L-?>9L?!L)?'}L?-L?L(L?iWL?a1L ?-XL ?TL ?/iL?L?yL?SL? SL?-qL?eK<L? PL?L?ML?ŖL?)L?iL ?-zKL?FR 7~~#!n7~#&]7~#S7~#7LL7LL/HoL#g%Z7L?.Z!LLLLLLLLLLLLLLLLLL LLLLL-?WL?(0L?^'XL ?WL? SL ?9VL?^L?L?Q L?ſL?;L?>L ?NL ?fL)?58L?L ?!IR7H7JJ#!|7KH#9V7LJ#!n7MH#^7NJ#e7OH#f7PJ#&]7QJ#MR7RH#^'X7SJ#eK<7TJ#7UW7VY7X|LZ? s|LV?.Z!LLL[a]L_LLLLLLLLLLLLL`2 LLa2LLc2LLd2LLe2LLf2-LLg-2)L-Lh)2L)LieLLjaLkLLLLLLLLLl2LLm2-LLo-2)L-Lq)2L)LreLLsawLLLLxaLzLL{L|L?xe|L?n|L?^'X|L ?4|L ?cV|L ?9W|L?bS|L? |L?S|L? S|L?|L? |L?ſ|L?Z|L?\|L ?9л|L-?5|L??|L? |L?k|L ?r9&|L)?m%|L?|L?!IR||7L/`S7 ^LL"rL 77L!L/HoL#g%Z7LL/HoL#g%Z7LL/HoL#g%Z7LL/HoL#g%Z7L%L/HoL#g%Z7L%L/HoL#g%Z7L L LLLL L-?(0 L?I L?f L)?" L?0 L?!IR  77#ſ7#(07#L(7#07#&]7#!I7#f7#-zK7#eK<7 #7!#T7"$LL/HoL#g%Z7#%L/jL/VhL'L)L+L-L.LL/L1L2LL L9vLLL-L;vL L LL=vL LL>vL LL?vLAL%L/HoL#g%Z7@CLL/HoL#g%Z7BDLELFL-?>FL?gFL?r!FL ?WFL?nFL ?WFL)?]FL?9VFL?1FL ?,(FL?FL?r FL?NFL ?9лFL?FL?CFL?fFL? FL ?58RFF6 66#K6#586#06#f6#W6#F6#T6#>#-6#"6#7.6#(06#L(6#&]6#6#N6#!I6#f6#-q6#-zK6#S6#eK<6LL6L?bLL6L?4LL^!6L?LLlq 6L?̢O6LLVLL%LVLL%LVLLLVLL%LVLLLVLLLVLL%LVLLLLvLLLvLLLLLLvL LLvLLL!LVLL!LVLL!LVL)LL)LL)LLLLLLLLLLLLLLLLLLLLLLLLLLL-LLLLL LL L LLLLL?uKL?tKL?qL?O$L?L?L ?uaL ?veL ?L?lYL?˿L?H L?&L?p>L?p>L"?ڥAL?~8L?9L ?9L#?Edž4L?W2L?1L?4L?x3L?VKL?a1L? L? >L-?bL&?L'?0L?n T<L?+L?EqL<L(?L)?9L!?YL,?OL$?L%?}r*L+?}~&L*?u2:L ?zL?L)?G9L ?LLR2mL/`S7L_O !LL)zL LVLFL)L)f%^L9rQfLLL LVLVLFLLLL!b L%b^X^UUL!fQLLR^?^^QL)ULL#LLL9VLLL-L=d^r#LL-L9"rL9=Qf=L'LV=LLL#LL9L=VLLL!b L%b^^QL9LLVL9^RLLL!b L%b^>LL*^UL!f3QL)QL?L)L9L=VL9LR^Q^^QL)QL-UL?L-L9L=VL9LLV9LL9^LLL9=LCLV=LLVL9=LL*RLLLL9LL^)^B^^__6_b__ ___$____'_P___wQL#LLLL=R_\QLL&LVLLV)LLLL(vLLVLL%LLVLI_ QLQf"LLVL-L)LV_^^QLL*LVL-L+LR_QLULL,LLL=Z_QLULLLLVL-LL-LVLLZ_`QLULLLLVLLLZ_2QLL.LVLLL=LLL/vL-LVL'LVLI_*LL!b L%b0^iQL)ULL0LVLLLLk^>QL)ULL1LVLLLVL0LVLLY^_pQLUL)*LL2LV)LL)L)LLVL!LVLLL!b L%b^#^ QL L3LV LLL^_QLUL)*LL4LVL-LLV5L)L L=VLLLk_QLULL6LVL-L#L L=LLVLLVLLLc_fQLUL)*LQLffLLV^ L&LVLLL9V7LL L=VLL-L9VffLLj^ LLj_QLL!b L%b^(L8LR^QQLL9LVLQ^_QLL!b L%b^(L:LR^QQLL;LVLQ^_gL<LJ_[QLULLL'LVLLQ_0QL!fL=LV_^^QLL&LVLLVLLL=LLLLL@vaLLLVLLZ^QLALLL=R^QLUL)*LLBLV)LL&LVLLLLLDvL)LVLLL!b L%b^/^,QL LELV LLVL LLV^LLVLY^RrL)LLLL%bLb/^{^wQf LGL9VLLLL=V^^PQLQf LGL9VL&L9VLLVLL)L=VLLVLL9VR^9^LLVLLLL=V-f L'L9VLLBRr9LL)L=V9Qf9L'LV9LLLL%f%QL-L9L=LLIvLLV^^^)LL-L93rLLLLL9LLL=V)LL)1RL7 7 |7F7L/`S7_8 L ^^^)^3^=^G^Q^[^e^n[L)LT"^b\L)LT"^V]L)LT"^J^L)LT"^>_L)LT"^2`L)LT"^&L)LT"^aL)LT"^bL)LT"^9LLz9LLz9LLz9LLz9LLzL ^^'^1^;^E^O^Y^c^m^w^^^^rL)LT"^sL)LT"^tL)LT"^zuL)LT"^nvL)LT"^bwL)LT"^VxL)LT"^JyL)LT"^>zL)LT"^2{L)LT"^&|L)LT"^}L)LT"^~L)LT"^L^#^-^7^A^K^U^_^i^s^}^^^^^^^L)LT"^L)LT"^L)LT"^L)LT"^L)LT"^L)LT"^zL)LT"^nL)LT"^bL)LT"^VL)LT"^JL)LT"^>L)LT"^2L)LT"^&L)LT"^L)LT"^L)LT"^9LL z9LL z9LL z9LLz9LLzLLL!b L%b^L-LT*^L-LT*^L^)^3^=^G^Q^[^e^o^y^^^^^^^^^^^L)LT"^L)LT"^L)LT"^L)LT"^L)LT"^L)LT"^L)LT"^L)LT"^L)LT"^zL)LT"^nL)LT"^bL)LT"^VL)LT"^JL)LT"^>L)LT"^2L)LT"^&L)LT"^L)LT"^L)LT"^9LL!z9LL%z9LLz9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9LLz9L-L-L-Lzrr9L-L-L-Lzrr9L-L-L-L zrr9L)L)L zrr9L-L-L-L zrr9LL z9LL z9L)L)Lzrr9LLz9LLz9L-L-L-LzrrLL-L-LVrL)LXLVrL-LRLLXL%zLVrL)LpLrL)LnLrL)LhLrL)LlLrLLLLVrLLLLL!b L%b ^V^QLL9LS^LRLLL9LRLLL9LL9RLLLL^)^+^?^P^i^^^^_F_p_____!_?_X_Z_)_QLLLZVL_QLLL_QLULLLLV_sQLULLLL)LZVLV_OQLULLLLLV_/QLLLLZVL_QLUL)*L)LL)LL)LV_QLUL)*L)LL)LL)LLL!b L%b ^V^QLL LS^LV_QLUL)*L)LL)L)L LV_`QLULLLLV_EQLUL)*L)L)LL)L LV_QLL%b L!b^ QQLLLSL^-^^QLL%b L!b^ QQLLLSL^-^^)^QLULLLLLV^QLLLLZVL^j)^fQLUL)*L)LLLL-LZVL)LLL!b L%b ^V^QLL LS^LV^RrLLLL!b L%b^^QLL91^LL9LLL9L9!RLLL^)^*^9^E^W^r^^^___%_=_h_______QLLLUB_QLL9_QLULLA_QLULLLLUJ_QLULLLA_QLLLLUB_QLULLLA_wQLUL)*L)LLLLL!b L%b^^QLL a^_*QL*LLLA_ULL9_UL*LLLA^QLL%b L!b^QQLLA^^^QLL%b L!b^QQLLA^^^^QLULLLA^qQLLLLUB^[^XQLUL)*L)LLL)LUVLLL!b L%b^^QLL a^^RrL ^^^^^^)^0^D^O^U^Q^M^I^EQLLY!^7QL^.QLLLWL^QLL숥^ QL^L ^^^!^#^%^'^)^+^-^/^1^3^5^6^2 ^. ^* ^&3^" ^ ^^<^^4^ ^^L^#^%^'^)^+^-^/^1^3^5^7^9^E^Q^X^g^q^m'^i}^e-^a^]&^Y^U^Q^M^I^EQLL9!^7QLL=!^)QL^ QLLL^QLL숥^7M|7NL/`S7U_;LL#9LLr9zL)QL)QL8VL-UL-UL-VLzrLLL&L)L!LL!LLL1L?L!L!LL z LrL!FL!FLUFLUFL-FLF L)F LF r*L* L)L3L)* L-*z*L!f*LUfVULL&LL)UL-QL!LUL$VL)*L-UFL)*L-UFLFL)ULF*L*LQL!L-QL*LL$VLFL!F*L)QL)LULL%VL)L-*L)F)L-*L)FRQL*L4VLL)*L%FL)*L%FL f LF ^L b* fLF L)*L%F rj8L9LL*L%FL*L%FLL=VL!l^*LLf L/aLf'^ ^L/*9L$L=L$LL)L,VLLL)LfLLLf .L5-L%L)L2Rr9L9LLf6=L)f&=L=*LL%F=L=*LL%F^=L=*L-F=L=*F=L=QL=*LL)V=L=*L-F=L=*F=L=* L-F !LL-f"=L=L)L4VLVLL%_RL+LL2LL!LL!LLLLL9LLLLLLvaLLL=LMvLj!L!LVNLL*Vl^nLLf L/aLLb LUDb6^?LfLL%LLL,VL-^^L-^ ^L/*LLf LQ^LL)L,VRrLj L6l^(LLf L/aL=L5^L/*!PL)L:VL2L-L:VL"LL-L-L"L0L(zRrRL9SL7LL9"U77|7F7L/`S7_L^^^#^-^7^A^JL)L^"^>L)L^"^2L)L^"^&L)L^"^L)L^"^L)L^"^9LL%z9LLz9LLz9L)L)Lzrr9L)L)LzrrLLlVL!L)LL!f^^^L!f)^^^L%fL%fQLLL-ULQLQLULL-fWL%L-fyLLL L9VL%z^1L)f-LyL-LL-L%zL9V^ LL9V^ LL9VR^"^^^^LGLc*rLLL!b L%b%^]-L9fyL9LLL%z^^=QL)QL-ULyL-L%LL-L%L-L=VL%z^rLL9L)LvaLL%f/QLf!QLULL%LLVR^^^^^LLL2L9LL9L=VL9rL9LL=VL9rLLL-zL9L!LL)LpVf^yLL-L%zLQL9"r9L)L)Ls+r9L9L%9L^^^7^n^^__QL9L=LLyLLLL%z襥^QL9L=LLLVLLyL)LyLLL%zL%zFR^QL9L=LLLVLLyL)LyLLL%zL%zFR^iQLULLLLVL:^IQLUL9L=LLyLLLVLyLLLVLL%zL%zF襥^r9L=LL)L)LV!LLLLL)L9L-LvaL9L)LvLL`2RLL%fUL%fxQLQL)ULQLQL)ULL)L!f$yLLLL9VL)L%zL=^yLLLL=L%zR^^^^^LL-LLLRrLLyL9L-L%zLL=LVRLLLL9L=VRLLL9RLL-LLyLyLLL%zLL-L9VLLL=LvLLVLL9LvLLVLVL%zLLLLfVL)LLVRrLLL)L9VLRLL-LLL-LiBRrLL9L)LvaL)LVLzLLL|VL)LLL=LLL9LvaLLL-LsVLLLLLLVLL\LLLdV}LL`VLgL)L`VR j9LLaVl^[LLf L{aLw f9=L=L=L%9L-L)LuVLyLLL%z^ ^L/*LL9Le"LLVLj9LLaVl^LLf L{aLw fx=L=L=L%9L-L)LuV-LLLLLLL)L`VLLLL`VLLyL-L-LzLLL%zR^ ^L/*!L9L9L)L=VL9LxLL9LyL-L=LzL9L%zR9L)LvLL_"rL)LL)Li2RrLLL)LjVLL|"L9LLLLL!L!LLLL-L-LvLLLLL=LLL LLvaLLLvLLmVL)LtLLLqVLLLLhLVL\LL-R QLL)LLL-f#9LL=LLvVLkVLL%_RLL]VLL)LvL-L_VrLL~f^L)LvVf^L%LL9"rQL!LL9"LL9LLvVL)L=*RLLLLLaLL)L`VL-L9LvL-L`VLLRRLL^9LL1.zLwLyLL)LL%zLLyL)LwL)LwLL%zRLLVL9L=L)Ls+LLLLnVLL)L%LLL\b2L+b3L*b4L?b5L[b6L]b7L-b8^>^:)^=^2)^5^*)^-^")^%^)^^)^^ )^ ^-L9)rLLf"QLULLL9LVR^^^LLLf"QLULLL9LVR^^^LLLf"QLULLL9LVR^^^LL"LL!f^^^)L)L*r9L=LnVL=L=L%L]fLbLlVL!f 9L_L-fLbLlVL!f 9LLLL!b L%b^VyL-LLL%zL)^+r)L)L)L/"3rLL#eL/@+r)L)L)L#eL/:37b[#F7cY#^'X7dW#S7eW#57f[#M7gW#cV7h[#i7iY#Q 7j[#!n7k[#e7lY#ſ7mZ#N7nZ#7oW#?7pY#;7q[#O177rW#\7s[#7tX#!I7u[#S7v[#T7wW#r9&7x[#!|7yW# 7z[#7.7{W#^'X7|[#iW7}Y#7~[#&]7[#MR7W# S7W#7X#f7LL!7VL?mL7VL?%PL7VL?57L7VL?zk35L7VL?3L7VL?3DLLyL)L!LL%zLLLLLaLLL)LvLL-LvLLLLLLeL)La)LLLLL L LLvLLLLvLLLLLL7VL?rL LLvLLLLLLL)L-L)2L)LeLLaL-L-L-LLLL LLLLv LL ?>UL?[L?f!9L?Z?ZL?3U=L-?3TL ?1L? L)?L? L ??B/L ?fL?8L?G;L??L? $L ?z2L?<<L?wuL?J.L?L? L?L?S5L? JR77  #O177!#m%7" #K7##587$# >7%#f7& #>37'#f7(#W7) #F7* #M7+ #S7, # S7- #>#-7. #7.7/#z270#ڥA71 #iW72#73#N74 #&]75#3U=76 #MR77 #S78 #eK<79# S7:<LL>7=L=?!L!L!L?zL@LALBLCLDLELFLGLHLILJLLLKvL)L)LOvLQLLTLUL-?+UL ?۰12UL?WUL?VKUL ?UL?1UL ?UL? >UL?5UL?:UL ?*VcUL?9UL?H>UL ?fUL?^UL)?.:RUU7OF7P7QQ#!|7RQ#O177SQ#K7TN#ſ7UQ#>37VP#C7WQ#MR7XQ#(07YN# S7ZcLL!7dMLd?n7L%7eMLe?|L7fMLf?3L7gMLg?~27Li7hMLh?7Lk7jMLj?|5Lm7lMLl?Lo7nMLn?*=Lq7pMLp?вʥLL!7ML?ǖAL%7ML?L7ML?;L7ML??L7ML?-L7ML?8gL7ML?=L7ML?L7ML?z L 7ML?L 7ML?!@L 7ML?NL 7ML? LL!7ML?4L%7ML?L7ML?i3L7ML?L7ML?8L7ML?b L7ML?e9L7ML?4wL7ML?"P=L 7ML?)L 7ML?L7ML?CiL7ML?nN L7ML?-L7ML??L7ML?q&LL!7ML?!;L%7ML?L;ʥLL7ML?N+L7ML?L7ML??L7ML?5`*L7ML?K9L7ML?\L7ML?^L7ML?,L7ML?4L7ML?-L7ML?}L7ML?#oL7ML? L7ML?:zL7ML?L7ML?طL7ML?L7ML?oc|L7ML?V/LLLLLLLLLLLLLLLLvLL ?d'L?"7>L)?':L-?L ?]UL?Ң8L?NL?SL ?L ?L ?_`L?I4L?ǚL? SL?ſR77 #ve7#ſ7 #q7#9V7#e7#>37 #G97 #lY7#7#F7 #a17#M7L LL!L"LLL)L)L$vaL%LLLL)LLLL LLLFveL-L-LLHvaL-LLJvLLLKvLLL?UJLL?a1LL?)LL?4LL)?-XLL?T LL-?LL?cLL?fR LL66|6L/`S7_(LLL!b L%b^L-L*^L-L*^L ^^^)^3^=^G^Q^[^e^nL)L"^bL)L"^VL)L"^JL)L"^>L)L"^2L)L"^&L)L"^L)L"^ L)L"^9LL%z9LLz9L)L)Lzrr9LLz9LLz9L)L)Lzrr9L-L-L-LzrrL ^^^)^3^=^G^Q^[^e^nL)L"^bL)L"^VL)L"^JL)L"^>L)L"^2 L)L"^&!L)L"^"L)L"^#L)L"^9LL%z9LLz9LLz9LLz9LLz9LLz9LLz9L)L)LzrrL^^)^3^=^G^Q^[^e^o^y^^^^^6L)L"^7L)L"^8L)L"^9L)L"^z:L)L"^n;L)L"^b<L)L"^V=L)L"^J>L)L"^>?L)L"^2@L)L"^&AL)L"^BL)L"^CL)L"^9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L-L-L-Lzrr9L-L-L-L zrr9L)L)L zrr9L-L-L-L zrr9L)L)L zrr9L)L)L zrrL^-^7^A^K^U^_^i^s^}^^^^^^^^^^^^^_L)L"^`L)L"^aL)L"^bL)L"^cL)L"^dL)L"^eL)L"^fL)L"^gL)L"^hL)L"^iL)L"^zjL)L"^nkL)L"^blL)L"^VmL)L"^JnL)L"^>oL)L"^2pL)L"^&qL)L"^rL)L"^sL)L"^9LL!z9LL%z9LLz9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L-L-L-Lzrr9LLLLLzrr9L-L-L-L zrr9LL z9LL z9LL z9LL z9LLz9L)L)Lzrr9L-L-L-Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)LzrrQLLLbLb^=^9QLLL9VR^+^#*LfLL9VR^^R^^rLL9"rLL%!LLzrLLL%zLL9LVLLLLbLbB^S^OQL%f,QQLfQUL!f ^,^^%^^^^^QLL9R^ ^L9L LLL9L9LLL-LL%zLL-LL%zL LLVLL=rLLLVLLL-LL%zLVf -L9^rLLLL)LVf )L9^rQLLLLL)LVf )L9^rLLLLLVLL)LVf )L9^rLLL!b L%b^L)^UL!fQLLR^^^QL)ULL!LVLLAbLZf)^@)L!LLLVLL!L-LLALLaLLLVLLlL-LL9VR^r!LLLL!zLf%LaLL)LL^ LLLLL!b L%b ^ ^^QLL)LL9LLLL=LLVRLLLLL9LL=VRL ^^^__@_h_____Lj$Lf LLVLQLVl^TLLf LaLw f2LLL%LL)LLQL%zF^ ^L/*L9L2_Lj%Lf L!LVULLVl^KLLf LaLw f)LLL%ULL)LV^ ^L/*L9L2_QLLL=LLLvL-LVLVL:_gULLLLLvL-LVLVL:_=QLLLLLL-LVLVL:_QLLL)LC^QLULLLLL-LVLVLLLLLLV^QLUL)*LLLL!bL%b^;^7^Y^/UL!fQLLL)LVLGR^4^^^LLLLLL-LVLVLL}LLVLfLLLLLLV^LR^rLLLbLb"^;^7QLLL)L9VR^0^LLLL=VL^^-L-L-L=;rQLL)LL9L=LLVL)zRLL9L=L-LVLRL!f_jLLVl_LLf LaLw ge LL9L-L-L)LVL-L ^^^!^4^S^{^^^_  _LWLV_)LL9L ^QLLL=LvLLVL^QLULLLL=LvL)LVLV^QLLL=LLVL^QLLL)L=VL^vQLULLL=L)LVLLL-L=VLV^AQLUL)*L)LLL=L-LVL L LL=VLV^^ ^L/*)rQL9L=L)L+L9L=L)L+Lf_L ^^^6^7^Q^k^^^^^QLLf)L)LL9^^QLLL=LvLL:^ULLL=LvLL:^QLLL=LL:^hQLLL)L=C^RQLULLL=L)LVLL)L=K^#UL*LLL=L)LB^r7|77U7F77#W7#K7#ſ7#9V7#9л7#W7#F7#!I7# 7#T7#!|7#L(7#7.7#N7#7#MR7#-zK7#(07#eK<7#?7#7#f7#9W7# S7LL!7L?L%7L?" LL!7 L ?Vf9L 7 L ?L7L?dL7L?L7L?$L7L?[NL7L?n4L7L?Kp7L7L?N?$LL!7%L%?ȩL'7&L&?7L)7(L(?j L+7*L*?+L-7,L,?EL/7.L.?JEA L170L0?L372L2?#{!L574L4? rDLL!7ELE?b L%7FLF?}wLH7GLG?կLJ7ILI?hLL7KLK?daL)L"^2bL)L"^&cL)L"^dL)L"^eL)L"^9LL!z9LL%z9LLz9LLz9LLzLL9L)L)LZg6zrrLLtVL8rLA^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^______ _ __________ _"_$_&_(_*_,_7_9_;_=_?_A_L_M!_I!_E!_A!_=!_9!_5!_1!_-!_)_%!_!!_!_!_!__ _ _!_%^QL!L쌥^QL!L쌥^QL!LL%^!^!^!^^!L^!^QL!L쌥^QL!L쌥^!^!^!^^^^^^|^x^t^p^l^h^d^`^\^X^T^P^L!^H!^D^@!^^`^u^_6_J_x___O__>_S_h_}___e_LLL9L)LJ:_*LLLL9L-LJVLF_lLLL9L)LJ:_ULLL9L)LJ:_?QL!g`QQLgOQQQLfUL%fUQLUULLf:LLLL9VLL9LvLLVL*L)LF^8LL9LvLLVLLLL9VL*L)LFR_^^^^QQQLULLfLLL9LLJVR_P^R^QQQLf\UL%fNUQLUULLL9LvLLVLLLL9VL*L)LFR_^^^^ ^^^^QLULLL9LvLLVLLLL9VL*L)LF_LLL9L)LJ:_zQL-fL-LL=VLL9LvLLB_JLLL9L)LJ:_3LLL9L)LJ:_QL*LL*LFL4L*L*L%zF LLLL9VL*LFL*LLL!b L%b^L?LV^ UL^F L*L%FLLL-L9VL*L%F_mQLULLL*LLF L*L-FLLLL9VL*L-FLF R_QLfUL*LLLLa)LLfL*L%FLLLL9VL*L%FLLLL9VL*L)f^%FRR_^^LLL9L)LJ:_zLLL9L)LJ:_cLLL9L)LJ:_LLLL9L)LJ:_5QLULLLLL9VLLL-L9\_QLL*LFLL9LvLLVL*LF_QLfL-LL=V_^^QL*LL#VfLL-LL=V_^^QL*LL*LALLzL*L)L)L S_GUL%frUUL!fbQLUQL)UQL-*LLLLLL-LLLLL,VLLL.VL9VR^ۥ^^^^QLUL)*LLLLL9VjLL0!l^*LLf LaLUDf%^ ^L/*LL*L)FLLL9LvL-LVLLL!b L%b^^QL L LL-L9V^L*L)F^rL ^^^)^3^=^Q^s^__-L/L92_-LFL92_-LGL92_-LL92_QLLLL9:_zQLLL)LL=VL L9:_VQLLL)LL=VL L9:_2QLLLLb@LbNLb\LbjLbxLbLbLbLb^^L!LL9V^^L%LL9V^^LLL9V^^LLL9V^^kLLL9V^l^VLLL9V^W^ALLL9V^B^,LLL9V^-^LLL9V^^LLLNL9J_QLjULLVLL *fuj*L LVl^QLLf LaLw f/ *L L *L%F L *L L-LVF^ ^L/*L LLL9V^0 *LL LL!fV^L%fX^L"L9Vl^HLLf LaLw f&L)LUL=VLLL L9V^ ^L/*9^rLEL^^ ^_ ^_4__QLLbLb_^QQLjULLVLL *fmj*L LVl^QLLf LaLw f/ *L L *L%F L *L L-LVF^ ^L/*LLg^Lil^@LLf LaLw fL)LUL9VLLk^ ^L/*R^^ r^^^QLULLL-L=VLHLVLmR^^UL!fGUQLf6QLUQQLLL-L=VLHLVLoR^_^^^^QLULLL)L=VLHLVLL-L=VLHLVqR^^L)LL*rL^^%^@^T^h^|^^QL-LLL92^yQL-L*L)LIL92^\QL-LLL92^FQL-LLL92^0QL-LLL92^)L*L9*^)LL9*^rL^^%^@^T^h^|^^QL-LLL92^QL-L*L)L"L92^uQL-LL L92^_QL-LL<L92^IQL-LL=L92^3)LHL9V)LL"L9V)LLL9*^)LL9*^rLLLbxLbLlbLbLbLbLbLbLbLbLbLbLbLbLbLbLc__LWL9V__L3L9V_^L L9V^^L:L9V^^LL9V^^L$L9V^^L?L9V^^LCL9V^^LL9V^^LTL9V^^rL L9V^p^bL-L9V^`^RLL9V^P^BL^L9V^@^2L1L9V^0^"LL9V^ ^LL9V^^L)L=2r-LLLbqLbLbLbLcLcLcoLcLcLcqLcLcLcsLcLcLcu__LL9VLLLL=VLLV__LLL=VLLLLLL=VL쭥_R_qLLL=VLLLLLL=VL쭥_ _?LHLV*LLL9VLLLVL*L-LILVLHLVLLL=VLL!LLLLVLLVLLVL!L"LVL%LLVR__LHLV*LLL9VLLLVL*L-LILVLHLVLLL=VLL!LLLLVLLVLLVL!L"LVL%LLVR__LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV__LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV_(_GLL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV__LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV_n_LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV__0LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV__LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV_W_vLL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV__LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV_^LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV_@^_LL9VLLLVLHLVLLL=VLL!LLL%LVLLVLLV_^-L-LzLLLbLbLb__QL!f0QQLfLLL=VLQLV_^^^^L!f1QLfLLL=VLQLV_H^_^_ _QL!f0QQLfLLL=VLDLV_^^^^L!f1QLfLLL=VLDLV^^^^^^L!f}QLfkQQL!fXQL!fGQQLf4QQQLLLL!LL2L!L=VR^W^^^^^^^^ ^^^LLL=VLHLVLLL=VLLL[r9L9*L%F9L9UL)L9*LVF9LL-L%+r9LLL=LLV9LHL)LLBLULL;L!L*L*L!L*LLLLLBL * L* LzLLL-LV*LLLL9VL)*L-*L L=VLLLLVLULL)L4L* L-LLLQzLQL%zF)ULLLZVLV)*L!fZ)*LLVLLL*LVLLL=LvLLVL)L L=VL*LL=J^LL L=BRrL)LL%fSL%fFQLUL)QL-UL-L)L)VL!f)LL9VR^^R^^^^^L-r9*Lf9L9*L)LL=V9L L=LLL9*f9L9*L)LL=V9*Lf9LHL=V_9LL9LHL=V9LLLLV9L%LOL=V9LL0)LLL!b L%b^i4LLLL%z^UQL)ULL9f"4LLLLL!L=VL%z^L4L-LL%zLL%L=S^r9LHL=V9LL)L#9LL)L=V9LHL9LL)L=V9LHL9LL)L=V9LHLL-LLLbGLbLbLbLc;LcxLcLcZLcLc_s_oL%f7UL!f&QLLL)L9VLL=VR_^_5^_/_+L%f7UL!f&QLLL)L9VLL=VR_Y^_^__L%f7UL!f&QLLL)L9VLL=VR_^_^__L%f7UL!f&QLLL)L9VLL=VR_^_i^_c__L%f7UL!f&QLLL)L9VLL=VR_^_%^__L%fkUL%fZUUL!fGQL)UQLLL-L9VLHL=VLL)L9VLML=VR_^_^_^__L%fkUL%fZUUL!fGQL)UQLLL-L9VLHL=VLL)L9VLL=VR_^_;^_5^_/_+L%gQL!gQQLgrUL!gcQQQLj *LL@Vl^6LLf LaLw fL)LLV^ ^L/*L*LLLLa*LL-L)VLLL L=LvL)LLV L=LLvLL]VLQL *f L *LQLL=VQL *f LHL=V_ LFULLL!b L%b ^3L4LLL *L%zF^QLLLL=V^ L=L LLLvLLVRR_^^^^ ^^^^L-LV__~L%g QL)ULLLLxL *fLfoLLLL)L)LvaLLL4LLL%zLL!LVL]VLVL L LL4LLL%zL LVR^mLf4LLL9VL=L9LvL)LVLLKL=V^2L9L=LvL)LVLLL9VLLKL=VR^ӥ^^i^eL%fVQL)ULL9L=LvLLVLL-L9VLLL!fLLL=VR^p^^^L9L=LvL-LVLLL-LVf#L-LL*L*L5VL=B^L-LLOL=BrLLL!b L%b^L L*^ULL!bL%b#^z^v)QL9L=L)LVR^^[)UQLfG)UUL!f4)QL-UQL9L=L-LV9L=L)LVR^;^^ ^^^QL)UL9LL-LVL1^9LL)L=V9LHL9LL)L=V9LHLLLLLL!b L%b^#9LGL=V^QL9LL)LV^9LHL=V9L9UL)L9*LVFR9*Lf9L9*L)LL=V9L L=9L!f69L9L%=*Lf=L=*L)LLV=L L9L!f69L9L%=*Lf=L=*L)LLV=L LLL9LHL=V9LL-LV9LLL=V9LVL=*RLLL9LL-LPL%3RL9L=LLLAL)VL!fL!RLLLLL%b L!b(^)QLL9L=L)LV9L^^RLLL9LVL=V9LHL=V9LL)LV9L-L=VLL9LRLLL9L=LL4LLL%zL'LV9LRLLLL)L9VL^)^;^^^__h___'___M__ _ _" _ _ _ QLLLL=S_ QL!fLGLV_ ^^QLUL*LLLLLLLva-LL*fL*L-LLVL)LLVL-FR_t QLLL)LS_^ QLULLL-LVLL<LR_5 UL%gUUL%gUUUL%g{UUUUL%ggUUUUUL%gQUUUUUUL%g9QLUQL)UUQL-UUUQLUUUUQLUUUUUQLUUUUUUQLUUUUUUULLL!f&QLfQQLf^^^ ^^^^gLLL!L L LLL!L4LL4L L4L L4L L4L L4L L L%zL%zL%zL%zL%zL%zL9VLLLfQLUL)LULLLL!LLLL4LLPLLL%zL+LVLLLL4LLLLVL4LL4L LL%zL%zL%zL9VLVL%LLVL)FRR^R^^LL LL4L L4LLRL!L4LLL%zL%zL%zL9VLVRR_ ^R^*^^^^^^^^ ^^^^QLL!bLbF^^QQLf-QQQL)ULLL-L-LLVR_B ^^s^oQQL)QUL-ULLLLvLLVLLLVLHLVL)L<LVLLLLVR_ ^QLULLLLvLLVLL-LVf"LLL*L*L5VLR^LLLOLR_a UL!f[UQLfJQLUQQLLL-LVLL!f^L%f^L=LVR_ ^^^^QLULLL-LVLHLVLL)LVLLLR_QLLLLvLLJ_*LL!b L%by^QL)ULLLLf LYLVLLLVLLLLL LLLVL  L-L VL L)iR^tQL)ULLLLf LYLVLLLLLVL LLLVL)L LLLVL aR^_QLUL)*L*LLLLVLLLLLLVL)LLV)LLL!b L%b ^BL-q^:QL L LL L L-LV LL LVL^R_QLUL)*LL LL4L *L *L%zF LLLVL LVL*LLL!b L%b^LLV^ UL^F L LL)LHLVUL L ULL *LVF L LLV L%LLV LFL)iR_QLULL)L)L[_QLUL)*LL LLLLL L zL/c_QLLLL!b L%b^2LGLV^&QLL*LL)VL!L)LV^*LLLvL*LVL*L*L LVLF_DQLLLL!b L%b^^QLLL)LV^*L*LL*LLLLvL*LV*L*fL*L*LLVL)FL4LL L L * L%zF R_*L*LL*LLLLvL*LV*L*fL*L*LLVL)FL4LLL L* L%zF R_QLULLL-LVLL)L[_QL!fLGLVLLV_^^QL&LEL)L]VL7VLj LL@Vl^ULLf LaLw f3LLLLULVLL-L)L V^ ^L/*LLL LVLLVLHLVLLLvL-LVL%LLZR_QLj *LL@Vl^2LLf LaLw fLLV^ ^L/*L*LQb*LLL)VL!f4LQL*LL[L-L[L *zL6VLLVL*LVLFLLLPF_CUL%ffUUL!fVQLUQL)UQL-*LLLLLLLL,VLLL.VLVR_^^^^QLUL)*LjLLLLLL LVLLLVLALVLLL-LVL LvLLVLLLV LLL!b L%b^$LGLV^QLLL)LV^L LLLL LvL)LVLL)LLVRl^LLf LaLUDfLLLVLHLVLLLLvL-L]VL)LLL!b L%b^9 LGLV^-QL L L L4LLL%zL'LV^L L L LLL LvL)L]VLL)LLV L%LLVR^ ^L/*Y^RrULQL)L-L9UL-L=LLLZVLVL=LVLL:R9LL-L"rL)LL)Rr9L=L LV9LHLV9LLLLL9LGL=V9LL=V9L)LL=V9L)L=LvLL*RLLLLBLLLLLLBLLzL)LLBLL;LL;L!L!L!L!LLLLLLBL!L!LL zL-LfULLLVLLLL9VLLL=VLQLL)VL!b LL!fQL* LLBLLBLLF L)FL!LLVL)L)LvLQLLVL!L-LLLVLBLLLL>VLLVLL vLLV-L)LVL-LVRUL*L)* L(VLVQL)URr 77|7U77L/`S74_KLLL!b L%b^L-L*^L-L*^9LLLLLL%zrrLLL%b L!b^*L^!^-L-L-L-LL9LL9LVL%LMr-L9LL9LLLfLLL%b L!b^QL)UL-*L*L-L9LL9f"-L-L-L-L L L L=VL=^qLLL%b L!bN^WQL)UL-*L*LLLLL=VL-L-L-LLLL=VL=^L;L^^L=LR^_L)Lf)LLL%b L!b^QL)UL-*L*LL9LL9f" L L LL=VL-L-L-L=^r-LLL%b L!bN^WQL)UL-*L*LLLLL=VL-L-L-L L L L=VL=^LFL^^LHLR^^!LLLLLLVL%L]RrLLL%b L!b9^?QL!fUL)*LL)R^$^^QLL9)^ L!^LLL%b L!bQ^WQL!f*LR^F^^QL)UL-*L*L-L9L-L-L-L=\^  L!^L)LL!f^;^^L!f)^*^^L9LLLL)L)LL=LTRrLLLLVL!LLL%b L!b^QL)UL-*L*L*L9LL=VLL!fL9LLLLu^@L!fLLLLLLt^)LLLLL-Lt^L9LLL%LE^)LLLL-LLL9L&vaLL)RrLLL%b L!bR^XQL)UL-*L*L9L-L=VLL!f L^L!f L^)^ L^LLL-L-L-L(va-LRrLLL%b L!bw^xQL)UL-*L*L9L-L=VLL!fL)LR^7L!fLLLLLd^LLLLLd^^LLL-L-L9LL=L*va-LL)RrLLL%b L!bH^HQL)UL-*L*L9L-L=VLL!bL!f^LI^^LLL-L-L-L,va-L)RrLLL!b L%b^;^8QL)UL-*L*L-L9)L)L=VL9A^LLLL.vaL!rLLL!b L%b ^U^QQL)UL-*L*L*LL9L)L9LLLL=L-LL}R^LLLL0vaLL)rLLL!b L%b ^>-^:QL)UL-*L*LLL9L)L=LL9^rLLLL2vaL-L)r47 7  #i7 #K7 #e7 #&]7 #F7 #7 #eK<7 # S7LL!7 L?mL7 L?"63LLLLLvLLLL)LeLLL!vaLLL"vL#L$L%LL'L)LLL+vL-L/L1L3L4L ?x 4L?D4L ?X4L ?4L)? S4L ?9V4L-?ſ4L?84L?L4L?\4L?9л4L??4L?:4L?f4L ?ͱJ4L?!IR447F77L/`S7Q_ LA^^^^^^^^^^^^^____%_/_9_C_M_W_a_k_u_______________ ___)_3_=_G_Q_[_e_o_y_______________hL)L?"_iL)L?"_jL)L?"_kL)L?"_lL)L?"_mL)L?"_nL)L?"_oL)L?"_pL)L?"_qL)L?"_rL)L?"_sL)L?"_~tL)L?"_ruL)L?"_fvL)L?"_ZwL)L?"_NxL)L?"_ByL)L?"_6zL)L?"_*{L)L?"_|L)L?"_}L)L?"_~L)L?"_L)L?"_L)L?"_L)L?"_L)L?"_L)L?"_L)L?"_L)L?"_L)L?"_L)L?"_L)L?"_L)L?"_vL)L?"_jL)L?"_^L)L?"_RL)L?"_FL)L?"_:L)L?"_.L)L?"_"L)L?"_L)L?"_ L)L?"^L)L?"^L)L?"^L)L?"^L)L?"^L)L?"^L)L?"^L)L?"^L)L?"^L)L?"^L)L?"^L)L?"^zL)L?"^nL)L?"^bL)L?"^VL)L?"^JL)L?"^>L)L?"^2L)L?"^&L)L?"^L)L?"^L)L?"^9LLz9LLz9LLz9LLz9LLz9LL z9LL z9LL z9LL z9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LLz9LL8z9LL9z9L)L)L?zrrL^^^#^-^7^A^JL)L?"^>L)L?"^2L)L?"^&L)L?"^L)L?"^ L)L?"^9LL!z9L)L)L%zrr9LLz9LLz9L)L)Lzrr9LLzLL?LA^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^}^z^w^t^q^n^k^h^e^b^_^\^Y^V^S^P^M^J^G^D^A^>^;^8^5^2^/^,^)^&^#^ ^^^^^^^ ^^^L^ ^^^^^^^^^^ ^^^^__{_yQLL9Lj+=LLBVLLfLLLLfl^?LLf L^aLw f=L)LLZVL-LUV^ ^L/*_^QLL9Lj+=LLBVLLfLLLLfl^?LLf L^aLw f=L)LLZVL-LUV^ ^L/*^^QLL9Lj+=LLBVLLfLLLLfl^?LLf L^aLw f=L)LLZVL-LUV^ ^L/*^^9L)L=LIV=L=L)Lf^%rLgLLAL9L)L)LvL-LMV!L-LbL%L!L=VLL)L=LvLLHVLLbL-LIVL)LzR9LL[V9L!LV9L!f|9Lf)=LLLLYV9L9LL^LLLL!fLf^!L=LLL9LLLYV9L!LL)RLLL9fDL=9Lf"LLL%LYVLLLYV^LL%L%LYVLfL=LfLL%^]LLL!fL fLLLLYV^,L-LLYVL-LLYVL-L LYVL-RLbLLLfL)LYV^7Lf)LL)LLLYVL)LLYV^ !LPL"LLMVL-LbLDV!L!L!LLL)LL-LL#va-LLL LLL$vLLMV)LQRrL^^2^b^^^^QL9L%LYV9LL[V9L!LV*^QLUL9LLYV9L=L-L\VL)LLD2^QL9LLYV9LLNLKV9LL[*^gQL9LLYV9LL[V9L!LV*^BQLUL9LLYV9L)L)L;^QL9LLYV9LLY*^9LL[V9L!LV9LLA^^^^^^^^^^^^____-_@_B_P_R_T_b_p_~______*_8_F_H_J_L_N_P_R_T_V_X_Z_\_^_`_b_d_f_h_j_l_n_p_r_t_v_x_________!_%___QL)L_QL)LL_QL)L_~QL)L_nQL)LL=_Y _UQL)LL _AQL)LL= _,QL)L _QL)L _ QL)L_QL)LL=__QL)L___QL)L_QL)L_QL)L_QL)LLL-L\VLLL\V_rQL)LLL-L\VLLL\V_IQL)LLL-L\VLLL\V_ QL)LLL-L\VLLL\V^^QL)L^QL)L^QL)L^^ ^!^"^#^$^%^&^'^(^)^*^+^,^-^.^/^0^{1^w2^s3^o4^k5^g6^c7^_QL)L8^OQL)L9^?:^;;^7<^3=^/>^+QLUL-L)L)L?^ L@!^LLL9fL)LLY:^yL fL!bL%fL)LL)LL%LY:^HL!fLfL)LLLYVLLY:^L)LLLYVLLD:RrLL-L&L[VL9LLL)QLLLbLDVL-LbLDVLLDVL)L=L'vLLMVL(L-LMVLL-LL)vLLHRRRr9LSLL!f =L_^=LLWVLLOLLL-L-L-L+vaL!R9L=L9f_Q=L>LL%L!fJf=L>LL%LL쨥^L%LLLbf /LPL)L!^LL!f`LLLLL!LL)f'LL)LLLIVLL%_LLL)L1R^LL!f3LL)LL)LLLIVL%L^O=L>L=L>LL-LL-LL)L LLLLIV-L%L)RL>LLLf^L)L>L)LLL쨥LL!f -LPLL9L.vLTVLLGL!L!L)L!L!L=VLLLL LLLLLLL0va!LLR 9L>L^^^^:^T^b^|^^9L=L ^^x9LGLLLLL V^c^Z9LFL9LLJVL^G^>9L=L^7^.9LLLLLLVR^^9L>L^ ^LcL!f^r9L=LLLj LLBVLLf Lcl^ALLf L^aLw fL)LLZV-L%L^ ^L/*)R9LL\VLLf LcL-rLL9L)L9LLQVL^6^4^2^0^.^,^*^(^&^$^"^ ^^^^^^^^^^ ^ ^ ^)^F^c^^~QLLL)L=VL^m^_QLLL)L=VL^N^@QLLL)L=VL^/^!QLLL)L=VL^^4LSLEVLX3RLL%f1QLUL9LL!LL=VL-L VLIVR^ ^^rkLLJVLL&f LcLGLLGLLGL)L!b)LbL!bLbL!bLf Lc)L L9L=L2vLTVLLgLLL L9LLLL3vaL-L%LL=VL!LdLLAL)LgL>LLL^ ^^-^T^f^dL)L^a^ULL%L)L^K^?LfLL>^L>L)L^"^LGL)L^^4LLEVLLLLA^^^^^^^^^^__ _P_X_`_h__________#_I_o_s_{________________________________ ___&_*_(_+_"_%_____L__LL__L__L__jLLBVl^/LLf L^aLw f Lc^ ^L/*L____LL__jLLBVl^/LLf L^aLw f Lc^ ^L/*L_O_FL_E_<L_;_2L_1_(jLLBVl^/LLf L^aLw f Lc^ ^L/*L____L______L__L__L__L]LLbLLL%zL__L]LLbLLL%zL_b_YL]LLbLLL%zL_:_1L]LLbLLL%zL__ _ _L_^L^^L^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^y^|^s^v^m^p^g^j^a^d^[^^^U^X^OL^N^EL^D^;^>^5^8^/^2^)^,^#^&^LL)LLV^^^ ^LcLLLLbLIVLL)Lf^%LLUVR_s)Lf Lc-L-L)LbLIV-L5LLL6vL-L@VLL7vLLHVLR l^KLLf L^aLLb Lt! b^Lc^^Lc^ ^L/*9L=L)L^^^\^p^^^QL.LLa^QLULL!fL=fL)LLIV>L)LeL?LLe^QL@LLaL^lQLALLa^ZQLULLLLLLLLLVBLL)LNLLbL`VR^QLCLLe^LLR+r9LFL)LNLfG^L-LNL LCVL-L=LzLR#LGLLer9L)L\Vf =L LVV=LJL-L-L:^t^r^p^n^p^^^^^b^^^^__$^T_1^P^N_@_S_f_y___^>___^6^4^2^0^.^,^*^(^&^$^"^ ^^^^^^^^^^ ^ ^^____QLlLLV__QLmLLV__QLnLLV__QLoLLV__QLKL숥__QLrLLV__QLLL숥__wQLtLLV_m_bQLuLLV_X_MQLvLLV_C_8QLML숥_2_'QLyLLV__QL|LLV_^QL}LLV^^QL~LLV^ޥ^QLLL)LV^ĥ^QLLL)LV^^QLLL)LV^^QLLL)LV^v^kQLLLV^a^VQLLLV^L^AQLLLV^7^,QLLLV^"^QLLLV^ ^LeLzLRVLL)Lf^%rLLL9LLL)QLL9LLbLRVL:LLbLRVL;L)LLbLRVL<LLRVLL=VLL)L)L=LDvLLHVLELLRVLLGvLLMVLHLLRVIL!L)L L-L-LLNvLLHVLOLLRsRRrQ757677|7879F7: 7;7<9#9V7=7#17><#K7?8#ſ7@9#f7A6#9л7B:#9V7C7#7D<#F7E7#p>7F7#97G9#Q 7H<#!n7I7#VK7J7#tK7K7#q7L9#ſ7M:#7N;#f7O<#eK<7P9#N7Q7#a17R7#W27S9#;7T9#!I7U7#ve7V;#07W9#W7X7#7Y6#!I7Z7#lY7[<#S7\<#!|7]<#7.7^;#(07_<#L(7`:#C7a9#7b<#&]7c<#MR7d<#(07e<#)7f6#f7gLL!75L?HL%75L?!L75L?Dm>L75L?L75L?Nƅ?L75L?gv)L75L?d]L75L?L?L75L?٣L 75L?|L75L?12L75L?WL75L?fL75L?,)L75L?#L75L?عyL75L?u L75L?0%L75L?oL75L?=:5L75L?q=L75L?D,L75L?%9L75L?B1L75L?L75L?j)L75L?L75L?J+L 75L? L!75L?P L"75L?1L#75L?`a?L$75L?>3L%75L?13L&75L?:L'75L?V?L(75L?V?L)75L?xn8L*75L?CEL+75L?1L,75L?{'CL-75L?<L.75L?:;L/75L?M>L075L?xE6L175L?BL275L?:L375L?;L475L?N L575L?@L675L?[/L775L?@;L75L? 8L75L?Nh7L:75L?#L;75L?#L<75L?@" L=75L?@" L>75L?\@L75L?n?L@75L?Ґ2 LL 7 5L ?$L7 5L ?ǀ L75L?Ɛ7L75L??L75L?AL75L?oLLg(75L?$<LLLLLL vL%LLLLL*vL,LL1LLLLLL8vLLLLLPvLQL?y/^QL?lYQL ?/ QL?ڥAQL-?pd{QL)?VKQL?77QL?p!>QL?IڕQL?4}rQL?UR QQ77#777#*=7#0%7#/ 7#?7#cV7#:7#J+7#Nh77#j)7#7#ſ7#,)7#:7#:;7#@" 7#W7#@" 7#CE7#7#7.7#5`*7#iW7#;7#Ɛ77#L?7#o7#MR7#%97#B17#7#9V7#K7#Nƅ?7#F7#W7#^'X7#@;7#q=7# 87#в7#!I7#\@7#d]7 #137 #A>7 #!I7 #{'C7 #>#-7#o7#N 7#7#عy7#B7#7#<7#7#ſ7#f7#H)Z7#ſ7##7#9л7#!I7#[/7# S7#Hwy7 #N+7!#gv)7"#?7##V?7$#!n7%#i7&#7'#A7(#e7)#u 7*#^7+##o7,#<7-#47.#!7/#70#xE671#772#`a?73#!|74#n?75#L(76#^'X77#&]78#K979#>37:#:7;#٣7<#127=#ſ7>#V?7?#9л7@#>37A#f7B#xn87C#P 7D#M7E#Dm>7F#H7G#=:57H#f7I#ſ7J#7K#|7L#@7M#W7N#D,7O#O177P# 7Q#~277R#17S#17T#$7U##7V#17W##7X#Ґ27Y#ǀ 7Z#(07[# 77\# S7]#M>7^fLLh7gLg?u_:Lj7iLi?ЗLl7kLk?w'Ln7mLm?B(Lp7oLo?˩L7qLq?!$L7rLr?>sLLu7tLt?;WvLwLxL)LzL{L|L-L}L)LLvL-L)LvLL-LvLLLvLLLLL LLLLLLLLLva LLvLLLLLLLLLLL)LLLv2LL2LL2LLLv2-LL)LLLL L Lv-2)LLLLL Lv)2LLLLLL LLLveLLLLLLLLLLLLLLLLLL+LLvaLL LL vL L?$ L?lY L? L?<* L?7 L ? L?z L?  L ?-8 L?]U L ?H> L?MP L?i L?c1' L ?) L?  L?) L?` L?̫( L-? L?7 L? L? L?0f L?s%o L? L?Q p- L ?`$R  6 U6 F6L/`S7_ L ^^^)^3^=^G^Q^[^e^nYL)LU"^bZL)LU"^V[L)LU"^J\L)LU"^>]L)LU"^2^L)LU"^&_L)LU"^`L)LU"^aL)LU"^9LL%z9LLz9LLz9LLz9LLz9LLz9LLz9LLzLWLLL!b?L%bLb@LbBLbDLbFLbHLbJ^Nc^J)Ld!^A)Lf!^8)Lh!^/)Lj!^&)Ll!^)Ln!^)Lp!^ )Lr!^L ^^^"^.^:^F^R^^^j^u^rQLLV!^dQLLV!^VQLLV!^HQLLV!^:QLLV!^,QLLV!^QLLV!^QLLV!^L/1&L$L9rL9L)LL)@rL/HoLt/Ho#QLSL#.Z!L-QL2L!fL%aLL)$e_LLLCRrLvL|L9LxL9LzL9L~LLVL9rL9L-L-L)L;rL7R|7S7TT#K7UT#-q7VT#eK<7WS# S7XbLL!7cRLc?]8Le7dRLd?99Lg7fRLf?f=uLi7hRLh?j0Lk7jRLj?ǁ=Lm7lRLl? Lo7nRLn?XILq7pRLp?Ls7rRLr?cmuLL/HoL#g%Z7twL%L/HoL#g%Z7vyL%L/HoL#g%Z7x{L%L/HoL#g%Z7z}L%L/HoL#g%Z7|L%L/HoL#g%Z7~LL/HoL#g%Z7LL/HoL#g%Z7L%L/HoL#g%Z7LL/HoL#g%Z7LLL)LL)LLLXLLLLLLLL LLL LL LLLL?q1L?L)?/L?۝L ? *5L?2L?RL?o>-L-?aL?sL?b$L?g|L?VL ?1&L ?MuL ?2lL ?NkR6L/`S7W_9-LLL!b L%b^L-L*^L-L*^*LL)*L%FLL*LL)*L%FLLLL)LVLLLLLLLLLLLL!b L%b^8 LML2^,QL)UL9L)f^L%LL=B^rLLVLLf0QLLLL)L va!L)L)V^^^ LTL*rLLL!b L%b^8 LbL2^,QL)UL9L)f^L%LL=B^rLLVL!f)^LjLLVl^2LLf LaLw f LZLV^ ^L/*LjLLVl^2LLf LaLw f L[LV^ ^L/*LLLVL!fLLL%zL9^OLLVLLf0ULLLL)Lva!L)L)V^^^ LiLJRrL ^^^^ ^ ^^^%^(^9^7^5^2^0^-QLL9^^^^*LL9^^-L9f+LLLLLLLL%zLV^L-L-LVrLLLLL-LVrLLL%f^!^^LLL-LL%zLL^^ ^ ^^^^^/^-!^+^'QLL^^QLL9^^%LLVLLfQL%fQQLLbLb@LbcLb^^QUL!fUL!f ^^^^^^QUL!fUL!f ^p^^h^^b^VQUL!fUL!f ^F^^>^^8^,QUL!fUL!f ^^^^^^^^^^)LLVL!fLQL9L-LV^L)LL%zLLj ULLVl^QLLf LaLw f/LLLVLLLULL)LV^ ^L/*LLrL)L ^^^_^s^^^^_?__L_VQLL!f#L!L9L!L-L9LV_,^^QLLL_QLf^L_QLLL_QLLL_QLLLL_QL*LLVfL=L!L9LV^ LL_QLLbLb#^<^8LLLLVL_W^LLLLVL_<^QLLL_%QLL%bL!b^^QQL*fxQUL!ffULfVUQLLbLb#R^p^8LLLLVLR^^LLLLVLR^^^^0^^*^^$^ ULLLLVLR^k^QLULL)L)LLLbLb^ ^QLR^^QLR^^ LLVLLVL^rLL9L=L-LVLL)LLVRLLLLLbLb3^D^@ULfQL)UQLL)R^-^^^QLL9R^^ LLVLLL=LLLVL9L-L-LVLRRjLULVL9l_LLf LaLw gQLLL=LL^^)^~^_j_o____O__I__,_ LLV_*f *Lf*^LLfLL^'LLLL-L9LL%zLLV_BQLULLLLLLVL L LLVLLL-LL LLLLLL%zL%zL%zLR_QLULg LLL9LLLL LVLVLLLLLLVLL LLLVLL%zLVLLLL9L-LVLLLLLLVL LLL VL L LLLL9LL%zLVLLVL LFLL!fLLV^!^^LLLVLLLVR^nLFfL)LV^VLLLL L9LL%zLLVLLLLLVLLLL%zL%zL_RUL%fUQLfUUL!f~QLUQQL)UQLLLLVLLLLLLLLL%zLLL L LL VLL%zL%zLR_^^ ^^^^QLULLL-LVLLLLLL L!vL)LVLL)L)L L LL VLLVR_KQLULLLLLVL)LL LV_QLULLLLLVL)L LV_QLULLLL"LLVLL LLLVLLL LL%zL%zLLV_QLUL)*LL LLLVL-L-L VL LV_cQLUL)*LLLLVL LFLLLLL#L LVLLLLL LL%zL%zL LVLL L LL VLL%zL%zL_QLULLLLLVL!L LVLL L LL L L$vL)LVLL)L)L L LL VLLVR_eQLUL)*LLL-LVL UL LLLLUL%zF L LLVL L)FLL LLLLLL%zLLLLL%zL%zLR^QLULL)LVLLL-LVL L LLVLL-L-L-LLVR^~QLUL*LLL LF LLLVL L-F L LLVLLL-LL LLLLLL%zL%zL%zLR^R^ ^L/*1rLLLLLL%zL9LL zL-f L=^9LLLLLVLfLL LLL LVLL LLL%zLVLLLLL LLL LVLL%zLVLLL LLLLLL%zLLLLL L LLLLL%zL%zL%zL%zLR^RrL9LL=LL L)LLLLLL zL/VL-LLLLLLL%zLL)LVLLL)LLLLLL%zL%zLRr(LL9LLLL9LL!f@L=LLLLL%zL%zLLVLLLL-LV^^^LL)LVLLL=LLLLL*LLVL%zL%zLLVLL)LLLLLLLL%zLLVLLLL-LVR-L9LLLLLL=LLL-LLLL+vL-LLL QL9LLVLLVLLL-L)LVRrL*LL%zL-LLVLLL)LVLLLLLLLLVLLLLLLLVLL%zL%zLVLL-LVLRrLL9LL-L=LVLLLVL)LRLL9L9L%=L LL-L9LLLzL/3RLLL!b L%b^6 LzL^*UL!fQLR^^^ULL9^L ^^ ^+^6^A^^___oLL_bLL_ULL_HLL_;ULLL9LLLL=L/vLLVLVLLLVL-LLLL%zL)LVLLLLLL L0vLLVLLLLLLL LLL%zLL-L%zLR^LL^|QLLL)LLT^cLL^V*LfLL^>^^QL*LLLL1aL-L)L-LLd^r9L=LL3L9LVLL=LLVLL=LLVLL%zL%zLVL9L)L)L!LLV9LL=LLVL=LLVLV9LL=LLLLVLLLLVLL%zL%zLVLL9L=LLLLL4vLLLLLL5vL)LLLLLL6vLLLL7b\L8bbL9bhLbnLbLbLcLc6LchL:cL;cL<cLlc__L__L__L__LLLL%b L!b^L^ L^_k_`LLLL%b L!b^L^ L^_2_'LLLL%b L!b^L^ L^__LLLL%b L!b^L^ L^__LLLL%b L!b^L^ L^__|LLLL%b L!b^L^ L^_N_CLLLL!LLV_,_!LLLL!LLV_ ^LLLbLb^^{L^^nQL)ULL L9L=L L=VLLLLVLLLLVLLLLVLL%zL%zL%zLVR^B^LLQL L LVL!LLVL L LVLV^4^)Lf Lf>L-^^^^L-QRr)LL!g+Q ^^^^ ^ ^^ ^___ QQL@f)LL%fQL!fxQQLfgUL!fYQQQLLLALVLVLLLLLLL-LLQLVLLR^^^^^ ^^^^ LLV_^_b_\QQLf)LL%fUL%fUUL!fQLUQLLLLLL%L9LL L=LBL LVLL LLVLLLLVLL%zL%zL%zL%zLVR^^^ ^^^^ LLV_^__QQLL!bL%c__uQULLbLbR_d_)QUQL@fLL%fQL!fxQQLfgUL!fYQQQLLLALVLVLLLLLLL-LL QLVLLR^^^^^ ^^^^ LLVR_^R_^)QUQLfLL%fUL%fUUL!fQLUQLLLLLL%L9LL L=LBL LVLLLLVLLLLVLL%zL%zL%zL%zLVR^^^ ^^^^ LLVR_^R_^_QQQL*gQQUL!gQULLbLbR__)QUQL@fLL%fQL!fxQQLfgUL!fYQQQLLLALVLVLLLLLLL-LL QLVLLR^^^^^ ^^^^ LLVR_^R^^)QUQLfLL%fUL%fUUL!fQLUQLLLLLL%L9LL L=LBL LVLLLLVLLLLVLL%zL%zL%zL%zLVR^^^ ^^^^ LLVR^N^R^^^^^^ ^^^^LLLVLLLLVLLVL9r9L9*L)LVF=L=L%LELL=LLVLLL)LL9L=VL)L9L=VL2RrL9LL="QLL^-^D^\^q^^_ __&___&_A_W____"_d__:_dQLLLL9VL_KQLLLL=VL_1QLLLVL_QLULLL-L-LLV_QLULL)LVLL-LVLLV_QLULLLDLLVLLLLVLLLLVLL%zL%zLV_|QL%f\QUL!fLQQLULL*L-LVFLL)LVLL-LL%zLR_"^^^^QLULLLL-LVLLELL-LLLLFvLLVL%zL_QLUL)*LL-LVLL-LVL)LLL!b L%b ^^QL LLVL^LV_[QLUL)*L-*L)Lf$L)LVLL)LVL LV^q-fCLLLLVLLLVL LVLLLL%zL^+LLLLL%zLL VLL%zL_QLUL)*LL-L-L-LL VL_yQLLLLVL-L VL_UQLLL)LL VL_8QLLLVL_ QL-LLGvLLVLLLHvLLVLL VL_QLLLL!b L%b^`LL V^TQL)ULLL-LVLL LLL L LVLVLL%zL%zLL V^L_aQLULLLLbLb7LIbNLb{^^L!LLLLVLV^^}L-LVL!LLV^m^_LLLLLLVLL%zLLVL^9^+LLLVLL%zLL VL^^ LLV_QLUL)*LLLLVL-L-LLVL_MQLULLLLLLLLLLLL%zLLVLLLVLLLLLLLL LLL LVLLLLL%zL LVLVLLVLL LLLLLLLzL/VLL LLLLLL%zLLVLLLLLLLLLL LLL%zL%zL%zL%zLL LLVLL)LVR_ QLULL)LVLLLV^QLULLL-L-LLVLVLLLLL%zLLLLL%zLLVLLL)LLLLL%zLLLL LLLLL L%zLLL LzL/VLVR^/QLULL)LVLL)LVLLV^LLRrLLfULfB*L*L9LL-LVL=L-LVLLVLLR^Q^^UL=L=*L)LVFLLL%zL9LVLL^^^ LL"QLLLfUL*L)*L)Lf9LL=VLLLLVL)LLVLLLLLL!LLVL-LVLLL)LL LL LL L LL!LLVLVLL%zL%zLLLLL-LV9L9*LLVFRR^^^ L+L*LLL9L=LLLKvL-LVLL-L9L=LLLLLvL-LVrLLL!b L%b:_9-LLVL!f%9LL=LLLLVL9L%z_QLLbL bY^^)QQLfA)QLUL*L)*LULLL L%zLLVR^^^4^0)QQL-ULLL-L-L%zLVR^s^QL)ULLLVL!f%9LL=LLLLVL9L%z9LL=LLVL9L%zLLB^r)*LLLL)LLL9LL=LNvaLL)VL-FL9RrLLVL9L9LQL=LRLVLLLLL9LVLLQL=LL%zL%zLVL9LLLLL-LVR9LL9LPL=LLVL-LLVLLL!b L%b^K L_LV^?QL)UL9LLL=L9LLL=LLVLV^LLL)LL%zrL9LLLLL!LLLzLQLLLL=LPLLVLLLLL9L=LSvLLVL)LL=LTvLLVLLLVLL%fQL^^^LLLL%zLLL=L9L-LUvLULV QLLLLLVL%zL-LLVLVLRrW7L/`S7 _SL^^^#^-^7^A^JL)L"^>L)L"^2"L)L"^& L)L"^ L)L"^ L)L"^9L)L)L!zrr9L)L)L%zrr9L)L)Lzrr9LLz9LLz9LLzLL9L)L)L?zrrL^^=^\^^^^QLUL LLLLVLLLL-LV^~QLULLL-LVL L숥^]QLULL)L9VL LL)L9V^3QL L숥^&QL L}L)LkVL ^ QL^rLLL9LLL)L#LL VLrL LL9"r LYL*rLf)*L-**L-L-LVFr***LFr*LL"rULLVLrLL!f -*^^^j*LLVl^6LLf LaLw fLLL9V^ ^L/*LL*LVL!f'*fL L-L=V**LL)LV襥rLLL!b L%b"^l& L9L=LVL LL*^OQL)ULjQL=LVl^/LLf LaLw f L^ ^L/*9^LLLLL9LL' va-LL!fL*LUL%zL)^#^^LLLL=VLL%zL)9rLLL!b L%b"^) L9L=LVL LL*^wQL)ULj9UL=LVLLLLL L*LVL!f^ zRl^/LLf LaLw f L^ ^L/*9^LLLLL9LLL* va-LL!fL*LUL%zL)^#^^LLLL=VLL%zL)9rLLL!b L%b"^, L9L=LVL LL*^kQL)ULj-*L=LVLL*LVL!f^l^/LLf LaLw f L^ ^L/*9^LLLLL9LLL- va-LL!fL*LUL%zL)^#^^LLLL=VLL%zL)9rLLL!b L%b^`9L L=L*^OQL)ULj*L9LVl^/LLf LaLw f L^ ^L/*9^LL-L-L9LL/ vaL*LUL%zLLLL)QLLLLLLrVL)LL-LLrVzRrL ^^^^ ^ ^ ^^^^/^-^+^(QLL9^^*LL9^^LL!f^L ^^^^^"^,^A^Q^y^^^^ LL*^ LL*^sQL-L9LL2^\QL-LL92^JQLULL9L)LVb LL9:^ UL*LL9L)L:^r)L)L9Vf)L)L VLL=V)L)Ll)L!f"Lf )L^)LL^Lf )LrLL?f:QL!f,QLULLLL VL VLL9R^^^^^LLrL ^^^^^^^^^^^^^^^^ ^^^L ^^^/^0^:^D^Y^i^^^QL-Lf)LL^^ L L*^v L L*^jQL-L9LL2^SQL-LL92^AQLULLL9VL9L)L:^UL-L9LL2^r9L)L)L=L4r9L)L)L=L4r9L)L)L=L4r)L)LVL!f_L-LLfQLLLLL9V_^^Lf QLLL)LL9V_g^^LfLLLL=V_F^^LfL-LLL=V_'^^L%f(QLLLLL=VLLV_^^L%f(QLLLLL=VLLV_^^LfLfQLUL)QL-UL-L)LVL!fSj L L9L7 vLLL~Vl^0LLf LaLLL)LV^ L/*R_:^R^^^*LLfLj LL L L9Vl^0LLf LaL)LL)LV^ L/*_^^^^Lfb*LLfLj L LL L9Vl^0LLf LaLL-L)LV^ L/*_g^^^^LLbLb_3_/LfQL)UL-QLUL-LL)Lfdj. L L9L8 vLLL~V LLL L9Vl^0LLf LaLLL)LV^ L/*R^^R^^^^LfxQL)QLLLLfSj L L9L9 vLLL~Vl^0LLf LaLLL)LV^ L/*R^(^R^^^^-L-L VL)L:r9L)L)L=L4r9L=LL-LL5LLf@QLUL9L=LLLLVLLLL%zL)LVR^4^^9L=LLLLVLLLoL)L;rL^ __,__QLUL)*L)LLL!b L%b ^'^#QL L L L-L L9VL^LL-L-LL=VLLLg7UL*LL)LLL!bL%b^^^ZL!f ^^^G^CUL!f2L%f#QL)QLLLL%zR^y^^ ^^^L%f?QLf/LQQLLL)Lf R^/^R^^^^^; LLLVL L LVLLL LLLrVL L)LrLLVL L LL< vLLL~VRR^^^ LZLrR_+QLj LLVl^YLLf LaLw f7f= L)L LLVLLL-L)LV^ ^L/*I^QLL!bL%b^J^F L:LV^^6QUL!f$QQLLLL-LL9VR^j^^^QLLLLLL9L> vL)LVLJ^2LLLLLL9LL? vaLL)J^rL^^/^K^g^^^__QLLLLLLK_QLLLLLLK_{QLLLLLLK_]QLLLLLLK_?QLLLLpLLK_!QLLL)LL9VLLLLLL)LrVL)LLLVLLLLkR^QLLL)LL=VLLL)QLLLL)LLL!bLb^'^#^8^QL LLLVR^!^ LLLL%zLLVLrVL-LLLVLLLLsR^!QLULL)L)LL\^rL^ ^^^)^2B L)L"^&C L)L"^D L)L"^E L)L"^LLVLLfQL%fQQLLbLb)Lb<^f^ZQUL!f G ^T^^L^@QUL!f H ^:^^2^&QUL!f-f I ^^^^^^^^^^J r9L=LLVLLL#LL9-L-L-L-LL vLLLLbLcfLc*LlcLcLcv Lc Lc L7c L8c> L9cp Lc Lc Lc Lc# Lc@ Lc] Lcz Lc L:c L;c L<c LcR _ _ LLLL9VLLL9VL^ ^W^^_^ ^^^ ^>L^5L^,pL^#LLLLL=VL^_H^ ^^^ ^>L^5L^,pL^#LLLLL=VL^^^ ^^^ ^>pL^5pL^,pL^#LLLLL=VL^^^ ^*^I^h^LLLLL=VL^yLLLLL=VL^XLLLLL=VL^7LLLLL=VLLLLL=VL^^_& _ LLLL9VLLL9VL^ ^W^^_^ ^^^ ^>L^5L^,pL^#LLLLL=VL^_H^ ^^^ ^>L^5L^,pL^#LLLLL=VL^^^ ^^^ ^>pL^5pL^,pL^#LLLLL=VL^^^ ^*^I^h^LLLLL=VL^yLLLLL=VL^XLLLLL=VL^7LLLLL=VLLLLL=VL^^_[ _D LLLL9VLLL9VL^ ^W^^_^ ^^^ ^>L^5L^,pL^#LLLLL=VL^_H^ ^^^ ^>L^5L^,pL^#LLLLL=VL^^^ ^^^ ^>pL^5pL^,pL^#LLLLL=VL^^^ ^*^I^h^LLLLL=VL^yLLLLL=VL^XLLLLL=VL^7LLLLL=VLLLLL=VL^^__yLLLL9VLLL9VL^ ^W^^_^ ^^^ ^>L^5L^,pL^#LLLLL=VL^_H^ ^^^ ^>L^5L^,pL^#LLLLL=VL^^^ ^^^ ^>pL^5pL^,pL^#LLLLL=VL^^^ ^*^I^h^LLLLL=VL^yLLLLL=VL^XLLLLL=VL^7LLLLL=VLLLLL=VL^^__LLLL9VLLL9VL^ ^W^^_^ ^^^ ^>L^5L^,pL^#LLLLL=VL^_H^ ^^^ ^>L^5L^,pL^#LLLLL=VL^^^ ^^^ ^>pL^5pL^,pL^#LLLLL=VL^^^ ^*^I^h^LLLLL=VL^yLLLLL=VL^XLLLLL=VL^7LLLLL=VLLLLL=VL^^__LLLLL=VLLLLL=VL-__LLLLL=VLLLLL=VL-__qLLLLL=VLLLLL=VL-_O_8LLLLL=VLLLLL=VL-__LLLLL=VLLLLL=VL-__LLLLL=VLLLLL=VL-__LLLLL=VLLLLL=VL-_k_TLLLLL=VLLLLL=VL-_2_LLLLL=VL-__LLLLL=VL-__LLLLL=VL-__LLLLL=VL-__LLLLL=VL-_~_gLLLLL=VL-_Z_CLLLLL=VL-_6_LLLLL=VL-__LLLbLb,__LLLLL=VL_(^QL)ULL)LVLLfQLjLM LLVl^2LLf LaLw f LLV^ ^L/*LLL)QLLLVL!fN LLO L LLLVL LLLL=VL R^^^ LLVR^Q^LP LVLLL LL)L LL=V LL-L LL=VLR^^LQ LVLLLLL)L LL=VLL-LLL=VLLL LLLVLLL LL LL%zL%zLVL-LLVR^^R LL L-LJr9L=LVLLL#)L)L)LT vL-LLLbLbLLIb{Lb^^LP LVLLLLL)LLL9VLR^^LP LVLLLLL-LLL9VLR^^LLLLL9VL-^r^dLL=VLLL!bL%b^ ^L^0^L^#^LLLLL9VL^^ LLBrLf9LL=L-LzL9L%z=L=L%L^ ^W^i^QLULLLLLL9VLLLLLL L LL L=VLLVL)R^QLLL^lQLV LLLLLLLL9L)LVLLLL!LLLLW vL-LV L)LVLR^rQLL)L9LLLL)LVLL9LLLLL%zL9LLVLL%zRrQLLVL!f L-LL!f+LLLLLLmVLzVLL%z^^^LLLL-LVLLLLL)L)LL9LLVLLLL!b L%b ^^ QL^L)LoLL!f^5^^LLY LL LL%zL)LVLLL|LLLL!b L%b^& L^QLLLL-L L=V^L LLLVL)LVL LLLLLLLLLzLQL%zF f LL)LVLLLf^L LR rLL!LLsLLgcL)LVL-g*-LLy^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~^|^z^x^v^t^r^p^n^l^j^h^f^d^b^`^^^\^Z^X^V^T^R^P^N^L^J^H^F^D^^@^>^<^:^8^6^4^2^0^^^^(^^$^"^ ^^^^^^^^^^^ ^^^____"_L__L_^L_^L^^L^^L^^L^^L^^L^^L^^L^^LLLL%z^^LLLL%z^^uLLLL%z^o^^LLLL%z^X^GLLLL%z^A^0LLpLL%z^*^LLLL%z^^[ L LL9V^LL%f L^^^)L-L%_)f[ L LL9V-LLL!bL%b^+^'^6^UL!fQLR^!^^^LLLoLZRrLL9LL-L=3R*L*L)*L-*LULQLLL9L=L9*L-Lf+9*LLVLf] LL^ Lt9LF9LL_ vLLV9LLVL9L-LV9L)F9LLLVL)LVL9LL)L LVLLL LLVL)L LVf9LLLVLLRRLLf9L=L-Lj3RQLLLVL!fFL)LF)L9L=LLLLL` vL)LoLVL-L)La vLL2RL)L9L)LL-LQL=V9L-LVLLL)LL%zRrL9L촥L-LLL%bL!c__L%gQL)UL-QLULL)LVLLg<QL%g.QQLj gQUL!gUL%gUUL!fUQLLL!fAQLf3QQL9LLQL=VL9LL)L QLV^^^^^LLVLLflQL%f^QQLj fOQUL!f?UL%f1UUL!f!UQL9LLL QLV^-^^^^^^^^ ^^^^k L LQLV^2^^^^^^^^ ^^^^9LLLLLVLL L%zLLL-LLVR^󥥥^^b^^L!f"LLoLVL-LLV^^^LLLoLVL9L)LLVLLVR^^L!fpLL-LVLLf@QLULLL LoLVLLLVLLLLVR^^^l L LLV^^^ LLBr9LL=L)L)LVQLL)LL9L=LLLVL)f^L=**L-L-L-LzLVL)LzRRLLLLL!b L%b ^ ^QLL9L=L-LLV^L=*UL)L)LLVL)RL9L촥LLL!b L%b?^-Lv L9LVfw LL L=L2^LL VL=L2^eQL)QQL-ULL-fL)R^D^R^QL)ULLLVLLLLL)L%zL)R^rLLL9L=VLLL9LLLVLLLL-LLVLRRLz LL{ L L9L="LLLf^^^L} LL"9L9L%L=L LVL=L)L=L-LiLVLLVL9L"LLLbLb^V^RQLL9R^K^?QLjL=LVl^$LLf LaL^L/*R^^LLLL^+^A^___&_5_=____9__k _ _ _M _ __)_QLLL)LL9T_sQLL!b L%b^LLLLK^QQL)QULL=LL-LVLLLLc vLLL-LL%zLLVLLL L LLVLoLLL LR^_ QLULL)LVLLLVLLfjQLj#Ld LLVLLL)QLRl^;LLf LaLw f-LL VLLV^ ^L/*^:^^L-LLVLLL)QL LLLLLVRLL-LyVLLLcR_ QL!gQQLLbLc@__QQQLLe bLf caLg cR__)UL%gC)UQL!g/)UQQLLbLbR_k_ -UQQULf-UUL!ft-UQQQLUQQUQLUQLLL LLLLL%zLVLL VL UL%zFLLL)LVR_ ^R_^R_^o-UUL!f\-UQQQLUQLLL LLLL%zLL VL UL%zFLLL)LVR_g ^R_a^^R_W^R_Q^)UL!f])QLLL)LLLLLLL L LLLL%zL%zLVLVR_ ^R^^T)UL!fC)QLLL)LLL *LLLL%zLVLVR_ ^R^^^QQQLh fsUL%fcUUL!fQUQLLLVLi LLLLLLVLzL{VLLLLVR_ ^^^^^^ ^^^QLULL)LVLLL!fLLLLL%z^^^)LLLLVLL-LVLLfKQLULLL L LLLLLm vaLLLLLVR^R^^LL Ln LLVL)LVL LL)L LVLLVL)L LRR_QLULL)LVLL)LVLLLL-LLVLLVLLL LL-LLLVLLVLL L{R_o L L-L:_u*LL!b L%bn^QL)ULL)LVLLLL-LLVL)LVLLLL-LLVLLLVLLLkR^QL)UL-*QLL-LVLLLL-LLVL-LVLL-LVL LL-L-LLV)L)L)LLVL)L L{R^_kQLUL)*L-*L*L L LLLLLL LzL/VLL襥_QLUL)*LL-LLLVLLLVLL m_QLULL)LL-LVLL \_QLULL)LVLLLLLLLVLL)L)LLLVLLLcR_gQLL!bL%b ^a^]LLLLV_8^EQUL!f3QQLLLVLLLL)LLVR_^^^QLLLLVLLLLp L-LVLVLLS_QLUL)*L*LL%f.QL*f!UL!fL-LL%z^&^^ ^^^^LLLLL%zLVLjg *LLVLLL)QL LLVL!f*L LVLL Lfq L L LLVLLzRl^LLf LaLw f*QLLVfr LL LLVLLLLs vLLVL-LLLVLL *QLL)LVLLVL!f *LL-LLzLVL-L)zR^ ^L/*LLL)QLL^ ^ ^%^R^~^zQLL-LL-LLV^`QL)L L LLLt vLLVL LLV^1QL)L L LLLu vLLVL LLV^L-LLfLLVL!f^  L-LLL-LV L!LLjV-LLL LR_1QLULLLL!b L%b ^$^ QLLLL L-L LV^L*UL-L)LLV)LLqVLLL[_QLj Ll^'LLf La LGLV^L/*LLLL)LLVLLL)QLL-LVLLfQL^^^ LKLVLLLL-L LLLLx vaLLLLLy vL LVL LL| vL-LVLLLLR _QLULL)LVLLL!f^^^~ L-LVLL)LLLVLLL)QLf)f L L LVL)LLVLL LR_IQLULL)LVLLL-LLVLLL)QL LLL LVL)LVLL L{R^QLULL)LVLLLLL vLLLLLL va)LLVL-L)LLkR^QLULLL-L-LVLJ^gQLULL)LVLLLL-LLVL)LVLLLL-LLVLLnVLLLcR^RrLLL!b L%b^*9L^ QLLLL9L-L=LV^L9L-L)LVrLLL)L9*RLLLLLLbLb_1_-QL)ULL9LL=VLLLLL vL-LLL!bL%b^C^? LLV^K^/UL!fQL)QLLLVR^(^^^ L)L LLVLVL LLLLLVLLVL-LVLLLVRR^i^PQLfBUL)*L-*L*LL LLLLLL LzL/VR^%^^^L9LL=BRr9LLVf L L=LV9L9L)LVjLLVl^RLLf LaLw f0LLL-L)LV-fL-L)LV^ ^L/*!rL9L촥LL9L L=LLLLLzL/VLLLj#L LLVLLL)QLRl^:LLf LaLw fLL VLL V^ ^L/*LL-L)LLL VLRR9QLLL^ ^P_._7QL9L L=LLLLLzL/VLLLL)LL-LLVL9R^QL%fhQUL!fXQQLULLLVLL-LVLLL-LQLVQLLL)LvLVRR^^^^^QLULLLVLLL-LVLLL)LVL-LQLVL LLVLL)QLL-LvLJR^ LL"^LLL)L9LL L=L vLL^^9^_^_"__a__)QL-LLf  L^ L)LV_QL-LL^^^#^+^3^;^E^O^XQL^NQL^DQL^:QL^0QLp^& LLV^ LLV^ LLV^_QL%fRQUL!fBQQLL L L L L LLzL/VLLLLL)R_1^^^^QL/n;$L L L L L L LzL/VLLVLLLLLL L-LVR_QLjLLl^'LLf La LLV^L/*L LLLVLLL)QLL-LVLLf;QLLLLLLLL LL9LL v LLV^^^ LLVLLLvLuVLR_QLUL)*LLLL!b L%b^TL^KQLLL LLLLLLzL/VLLLLL)LLR^LLL LLL LVLLL)QLLLL!f[LL!bL%b0^G^?L LLVLLLLLrV_^ L LL9V_^^^LL!b L%b_ L LL9V_QL)QLLLLLLrVLL)LLrVL LL%fQLLbL!bp^^QQL)QLLfILLLL%zLLVLLL-LLLL%zLLR^^R^4^(QQLfQLL R^Y^^ ^^^LLfQLLLLV^!^^LLLL%zLLVL LLL-LLfQLLLLV^!^^LLLL%zLLVLLL-L LV LLLVLR^R_0QLULL L L L LLLzL/VLLLLLVL L-L)LLLVL)LVLR^QLULL L L L L LLzL/VLLL L)LL LLL LVL LVLLVL)R^lQLUL L LVLLL LLL L L LLLL L L v LLVLL)L VLLVLR^LLLLL)RRr LL L L9L=LL9L L=LLLLLLzL/VLLLfLL^8-LLVLLLVLLL vL)L)LVLVRL)LLLVR9L)L)L=+rQLL)LLLLLL9L)L=LLLLLLL v LLVL=LL=LL vLLVLLL!b L%b ^2^.QL=LLVL=LLL-QLVL^L=LLVL=LL L-LLV=L-L VLLzRRLL%fQLLbL!b^^QULfQQL)QUQL-UL9L-L-LL=VLLL)QLLLVL!f^8L)LVLLfQLL L^^^ LHLVRR^^^^ ^ ^^^-LL9LL-L=LLLLLLLL v L-LVLLLL vL-LVLLLLL-zRr9LL)L9L%zr9LL-L9L%zrLLLL*LL%zLVL!b*LL*LL%zLVf^LL*LL%zLL%zLLL L*LVL L*LVLLzRLLL!b L%bc^j#9*L}L=LkVLVLLl^;LLf LaLw f LL LLV^ ^L/*)^MQL)ULjLLLLVLl^$LLf LaL^L/*9^L LVLLLLLLL9LL va*L1RrL)L9L-L=VLLL)LL%zRrj*LLVl_LLf LaLw g-L-L-L9VLLLj  *LL*LL%zLVLl^9LLf LaLw f*L-*L^ ^L/*LLLLLL-LL%zL L *LLLf *^. LLLLLLLLLLLLzL*L*L*Lz L*LL)*LVLL}LLLL%L!LwVLL=f  LL{VLL%fQL%fuQQLQUL)LL-LVLLL vLLL-LL%zLLVLLLLLoLLLLVRR^^^^^-L)LVL=f  LL{V-*LF-L*L)L*V-*R ^ ^L/*)r9LLLLLxVL=VL!r9*L9**L-L-LVFr!LLL LLLLL*LL%zLLLLLLLLLLLLzLLLLLLz LL9L vLL LLLLLLLLLL%zL%zLLVL LL%zLQ LLLLLL LLL LLLLQ LmVLL%zL%zLLLLL%zL%zLLV@L-LLLLL%zLLLVL)V)LL*LL%zLL=VL-Rr 7X7Y|7Z7[7\L/`S7 _ L^^^'^1^;^E^O^Y^b_L)L "^V L)L "^JL)L "^>`L)L "^2L)L "^&bL)L "^ L)L "^ L)L "^9LL!z9LL%z9LLz9LLz9LLz9LLz9LLz9L)L)LzrrL ^^%^/^9^C^M^W^a^k^u^^^rL)L "^uL)L "^zvL)L "^nwL)L "^b|L)L "^V}L)L "^J L)L "^>L)L "^2 L)L "^& L)L "^sL)L "^ L)L "^L^+^5^?^I^S^]^g^q^{^^^^^^^^^^^^L)L "^L)L "^L)L "^L)L "^ L)L "^L)L "^L)L "^L)L "^L)L "^L)L "^zL)L "^nL)L "^b L)L "^V L)L "^J L)L "^>L)L "^2L)L "^&L)L "^L)L "^L)L "^9LLz9LLz9LLz9LLz9LLz9LLzL^ ^^^)^2 L)L "^& L)L "^ L)L "^ L)L "^9L-L-L-L!zrr9LL%z9LLz9L)L)LzrrL^ ^^^)^2 L)L "^& L)L "^ L)L "^ L)L "^9LL%z9LLz9LLzL^ ^^^& L)L "^ L)L "^ L)L "^9L)L)L!zrr9LL%z9LLzL^ ^^^& L)L "^ L)L "^ L)L "^9LL!z9L)L)L%zrr9L)L)LzrrL^^^'^1^;^E^O^Y^b% L)L "^V& L)L "^J' L)L "^>( L)L "^2) L)L "^&* L)L "^+ L)L "^, L)L "^9LL!z9LL%z9LLz9L)L)Lzrr9L-L-L-Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)LzrrL^ ^^^& L)L "^ L)L "^ L)L "^9LL!z9L)L)L%zrr9L)L)LzrrL^+^5^?^I^S^]^g^q^{^^^^^^^^^^^^L)L "^L)L "^L)L "^L)L "^L)L "^B L)L "^L)L "^L)L "^L)L "^C L)L "^zD L)L "^nE L)L "^bF L)L "^VG L)L "^JH L)L "^>I L)L "^2L)L "^&J L)L "^K L)L "^L)L "^9LL!z9LL%z9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L-L-L-Lzrr9LLLLLLzrr9L-L-L-Lzrr9L)L)L zrr9L)L)L zrr9LL z9L-L-L-L zrr9L)L)L zrr9LLz9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)Lzrr9L)L)LzrrL^^^3^B^I^]^d^k^QLL !^QLLL L^iQLf^^XQL^OQLLL L^9QL^0QL^'QLUL}L)L VL}LL9𭈥^LL!f^^^}L-L VL}L)rL ^^^^!^#^%^'^)^+^-^/^1^2^. ^*3^& ^"4^^w ^x ^y ^z ^  ^{ ^L^+^-^/^1^3^5^7^9^;^=^?^A^C^E^G^I^U^a^h^w^^}'^y}^u-^qA^m&^i^e^a^]^Y^U} ^Q^M~ ^I ^EQLL9!^7QLL=!^)QL^ QLLL ^QLL숥^ 7 U7 F7 7  #K7  #iW7  #C7  #(07  # 7  #7  LL 7  L ?7L 7  L ?,L 7  L ?J+L 7  L ?|5L 7  L ?L 7  L ?вL 7  L ?ϱpL 7  L ?̨g LL!7  L ?ǖAL%7  L ??L7  L ?-L7  L ?8gL7  L ?!@L7  L ?NL7  L ?7L7  L ?%PL7  L ?.7L 7  L ?9L 7  L ?L 7  L ?/l LL!7  L ?4L%7  L ?L7  L ?i3L7  L ?L7  L ?L7  L ?b L7  L ?e9L 7  L ?4wL7  L ?"P=L 7  L ?)L 7  L ?L 7  L ?8L 7  L ?xL 7  L ? L7  L ?[L 7  L ?CiL 7  L ?nN L 7  L ?-L 7  L ??L 7  L ?q& LL 7  L ?%L 7  L ?1L 7  L ?L 7  L ?D\ϥ LL!7  L ?GAL 7  L ? ^L 7  L ?L 7  L ?Х LL 7  L ?iT++L 7  L ?s&L 7  L ?( LL 7  L ?=.#L" 7!  L! ?(O9L$ 7#  L# ?/ܥ- LL/ 7.  L. ?3L1 70  L0 ?FL3 72  L2 ?'L5 74  L4 ?61L7 76  L6 ?%L9 78  L8 ? K1L; 7:  L: ?*L= 7<  L< ?vĥ> LL? 7  L ?=.#L@ 7!  L! ?(O9LA 7#  L# ?/ܥL LLN 7M  LM ?N+LP 7O  LO ?LR 7Q  LQ ?5`*LT 7S  LS ?K9LV 7U  LU ?\LX 7W  LW ?O-LZ 7Y  LY ?4L\ 7[  L[ ?}L^ 7]  L] ?#oL` 7_  L_ ?)Lb 7a  La ?' Ld 7c  Lc ?Lf 7e  Le ?]Lh 7g  Lg ?MLj 7i  Li ?@\ Ll 7k  Lk ?,Ln 7m  Lm ?-Lp 7o  Lo ?%Lr 7q  Lq ?Lt 7s  Ls ?, LLLLu av L| L)LL vL L?"7> L-?1m L)?': L? L?]UR  7]L/`S7 _eL^ ^^^& L)L "^ L)L "^ L)L "^ L.L "rLL9"rL L VL!f^L L VL!f^ LL "rLL LL "rLL%fPQL!fAUL!fQL^=^^QLULLL)L9VL VR^^^^^)L)L *rLL "rLL f4QLUL)*L)L)LLLL9VL VR^(^^LLf^LL)L CrLL f4QLUL)*L)L)LLLL9VL VR^(^^L-L!f^LL)L CrLLL9LRLLLL^^$^0^E^g^^^^QLL L ^QLL ^QL9LL VL ^QLUL9L L)L VLL V^QLUL)*L)L)L)LLL!b L%b ^ ^QLL9L ^L V^PQLULLL9L V^4QLULL9LL V^QLULLL V^RLLL bL b^ ^^^*LL9R^ ^L^^ ^0^@^P^`^p^nQLL ^j^\QLL ^X^JQLL ^F^8QLL ^4^&QLL ^"^QLL ^^ L~L "L)LLL!bL%b^%^!L!f  ^'^^^  ^^L%f ^^^ rL)LL!f^^^L!f ^^^ rLLLLbL!b^ ^ULL9R^^ ^^L)LL LL-L%zRrLLL!b L%b^' LL 2^ULL LL L%z^rL L)L L%zrL9f=^3f LL V^ LL VL LL-L%LL%zLLL!b L%b^T LL B^HQL)ULLLL-L LLL va!LL LL L%z^rL L9L)L=L VLL%zr)LLL!b L%b^I LL B^=QL)ULLL vLLL L VLL LL L%z^rj"LL VLLLL9Vl^ALLf L aLw f L)LLLL%z^ ^L/*1r LLL L-L%zrL9LLL!b L%b ^8 ^4QLLLfQL^^^ L)L L%z^LL%g9QL%g*QQ^^ ^ ^^^`^__ QQQLQUL)QL-ULL9LLLL)LL=LLVLLVRR^ڥ^QQQLQQUL)QUL-QLUL LL=LLVL LLL%zL)L%zL9R^w^OQQQLQUL)QL-UL L)L LLL%zL)L%zL9R^(^^^^^LLLL LLL!b L%b^P LL *^DL)LQLULLLLL9L=LLL vaLI^LLL!bL%b_@_< L9L L z_>_'QLL!bL%bR__  LL VR__)QQ^^^x_R__{__-R__)QQQL-QULQLULL=LLL)QLLL9LLL LLL LVzRR__p)QQQL!fW)QUL-QLULL=LLL)QL9LL)L LLLVLLzRR_-^^)QQQL-QULQLULL=LLL)QLLL L9LL)L LL L L VLVLLzRR__)QQQL-QQULQULQLULL=LLL)QLL9LL LL)L LL L L VL L VLVLLzRR_1_ )QQQL-QQULQQ*LQULQLULL=LLL)QLLLLL L9LL-L L L L VLLL L VLVL)LzRR__g)QQQL-QQULQULQLUL LLLL VL LLL%zL)L%zL=R_'_)QQQL-QULQLUL L)L LLL%zL)L%zL=R__)QQQLL%bL!cR__-QQQQLL!bLbR_y_QQQQQLQQQULQQULQQLQULQLULL=LLL)QL LLL VL9L%zL L)LL L L L L LLL%L VL L%zL%zL VLLzRR __)QQQQQLg,QQQQQLQQQQULQQQULQQULQQLQULQL ULL=LLL)QLEL L L LL VLL VL LL L L LL VL VL L-L9L%zL%zL L-LL L LEL L L LL L LLL!L VLL%zL%zL%zL VL)LzRR _^^QQQQQLQQQQULQQQULQQULQQLQULQL ULL=LLL)QL LLL L LL VL VL9L%zL L)LL L L L L LL!L VL L%zL%zL VLLzRR ^^^w-QQULQQLQULQLULL=LLL)QL9LL)LL LL VLL VLLzRR^-^^^^L L9L)L zLLL!b L%b^i LL *^]L)LQLULLLL)L9L=LLLLL LLLL L L vaLI^LLL!b L%b^z L L z^nQL)QL-ULL9LLL)QLL=LLL)QLL)LVLL)L VL LL LL%zzR^LLL!bL%b^^ L L9z_^oQL!f]QL)UL-L)L=f.L)LLLL)QLLLLLVzR^  L)LzR_t^^^L!f LCL V_O^^L%fQL%fL%fQQLLf)LLLLLLLL)QLLLLL)QLL L VL!f LLLL VLz^/L L VL!f ^ LL)L VLLLVzR _^^^^ ^^^^L%gcQL)LLL!f"L!fQLL_+^^^^L!f\LQLLLLL)QLLLLL)QLLL)L VL LL VLLVzRR^ĥ^^L!fLQLLLLL)QLLLLL)QL LL VL fL L VL!f# LLLL VL LL Vz^,L)L VLL)L VL LL VLLVzRR^^^ L\L V^^^ L_L "9L L)L L%zQLL)L)L9L)LLL!b L%b ^)^QLLL=V^LL L-L BRR9L=L vL)L VL L L L L%zLLLLLL)QLLLL!b L%b^B^?QLL!b L%b^$ LLV^)QQL LLV^^)L)L L VL!Rr 7 |7 7  7 7  #JEA 7  #K7  #в7  #>37  #7  #n7  #F7  #77  #F7  #*7  #-;7  #M7  #cV7  #}w7  #e7  #կ7  #617  #E7  #{7  #-q7  #^_7  #O177  #\7  ##{!7  #h7  #v7  #+7  #l7  #7  #7  #j 7  #r9&7  #!|7  # 7  # K17  #7.7  #!v7  #iW7  #o7  #d<7  #}7  #%7  #MR7  #b 7  #[ 7  #'7  #ȩ7  # r7  # S7  #7  #7  #?7  LL!7  L ?_'L%7  L ?a7L7  L ?գu L LL L L L L LLLL a LLLLL-LL -2)L-L )2L)L eLL a L L LLLL a L L L L LL L L LL L LLL vLL LLLLLL LL LL v LLLLL-LL veLLL-LLLL LLL LL!LL v aLL)LL vL L?S2 L? L?QD& L? L?< L?׬ L?! L ?.)j L ?h L ?LL\3 L ?[  L?9V L?f L?i L?1( L?7 L?C< L?o$ L?ī L?Jp L? L?=p L?n L?) L? L)?)Dd! L-?]K L? L? L?qY}  L ?_ L?StR   7_7aU7b47cF7dL/`S7 _4 LLL!b L%b^L-L *^L-L *^9LLLLL%zrrLLL!b L%b ^!^ *L^)L)L)LL9LL9L VL%L ^GQL)UL-*LLLL=VL)L)LLL=VL=^ L=L r^^ L?L J^^L)Lf)LLL%b L!b^QL)UL-*LL9L-L9fLLL=VL)L)L=k^b)LLL%b L!b>^GQL)UL-*L L LL=VL)L)LLL=VL=^ LIL r^^ LKL J^^LLLLL VL%L LRrLLL!b L%b^W LTL *^KQL!fUL)*LR^5^^QL)UL-*L)L9L)L)L=K^LLL!b L%b^I L ^@QL!fUL)*LR^*^^QL)UL-*L)L9^L)LL!fL^6^^L!fL^^^)L)L9L-L=L3rLLL!b L%b^} LL L%L L^kQL)UL-*LL)LVLL!f^=L!fLLL9VL-L-L=c^-L-LLLL9VL=c^rL-LL!fL-L-L9V^^^L!fL-LL9V^^^L%fL%fQLUL)*L-*LQLUL*L*LLLf LL LL L L=VLV^8LLf  L L LL=VL-L-LV^ L L LVR^$^^^^ LL :rL)LL!fL^9^^L!fL^"^^-L-L-L9LL=LDrLLL!b L%b^L L z^QL)UL-*LL)LVLL!f L)Lz^pL!f7LLL9VLLL)QLL L)LLL=VLzR^4LL-L9VLLL)QLLL LLLL=VzR^rLL-L9VL)r L L LL!f^^^LLL!b L%b^B^?QL)UL-*L9L)L=VLL!bL!f-^LA^LL)LL-L va)L!rLLL!b L%b ^l ^hQL)UL-*L9L)L=VLL!f-L)LJ^1L!f-LL-L-LS^-L-L-LLS^LL)LL9LL=L va)LL-rL)LL!fL_5^^L!fL_^^L%gL%fQLUL)*L-*LQLUL*L*LLfWL%f9L-L L=V^?9LL LVLLL)QL9L LLVL L LLVLVR^VL%f9LL L=V^?9L-L LVLLL)QL9L-LLVLL-LLVLVRR^$^^^^ LL *rLLL)L9L=LLL va-L-L)VL)RrL)LL!fL ^^^L!fL ^ե^^L%fQLUL)*L-L9L-L)L=VLLLbLb9^k^gL)QL9LL-LVLL-LVLVR^H^6L)QL9LL-LVLLLLVLVR^^ LL VR^^^ L L *rLLL)L9L-L=LL va-L-L)VL)RrL)LL!fL ^^^L!fL^ե^^L%fQLUL)*L-L9L-L)L=VLLLbLb<^k^gL)QL9LL-LVLLLLVLVR^E^3L)QL9LL-LVLL-LVLVR^^ L L VR^^^ L#L *rLLL)L9L-L=LL va-L-L)VL)RrLLL!b L%b^0^-QL)UL-*L)L9L=L99^LLLL vaL!r 7 7  #i7  #K7  #&]7  #F7  #7  # S7  LL!7  L ?mL 7  L ?"63 LL LLL vLLLL)LL veLL aL)LL vLLLL)LL veL)L)LLL va-LL)L vLLLL)LL veLL a L L L LL L vLLLL vLLL L vLL L L vL L L?e& L ?n L?c L?D L?H L ? L?9V L ?3e L?ſ L?8 L-? > L?L L?? L?: L)?\i L?f L ?  L?ͱJ L ?x L?!IR  7eL/`S7 _`OLLL!b L%b^[ L-L? *^\ L-L? *^9LL!z9LL%zLL? 9L)L)LL4zrrLLL!b L%b^!QLe LL+ ^QLf L숥^LLc VL9 rLLLbLbLbLbLbLbLbL<bLbLbLbLbLbLbLbLbL:bL;bLi bLbLbLbLbLlbL8bL7bL9bLbLbLbLb^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^z^x^r^p^j^h^b^`^Z!^X^R!^P^J%^H^B%^@^:^8^2^0^*^(^"^ ^^^^^ ^^L9LL9LLf^Lf )L^RrLLLLLfaQLUL)*L)LL9Vf<LL-L=VLLQ L)LQ LVLL)LL# VR^6^R^ ^^LQ LLQ LVLLLL# VRrLLLLLfAQLUL)*LLL9VL-L LL L=VL-L# VR^#^^L)L9VLLL VRrLLL!b L%b^o-L; LLA L: V^[QL)ULL6 LL0 LL0 LLL9VL; L%zL%zL< LL; LL-LN L: VR^rLLL!b L%b^h-LL7 L ^UQL)ULL6 LLLL7 L L0 LL0 LLL9VL; L%zL%zL& V^rLLLbLbLIbLb^"^^^^^^^ ^^L` LL9rL)rLLJ Lj L9l^$LL! f L-LJ f^S^L/*Lj L=l^%LL! f )LLJ fR^^L/*L0 L)L)L%zR^L!LL VL%fSL!LL VL)L%L@ Vj L=l^%LL! f )LLJ fR^L^L/*LR^E^^L!LL VL!f!L!LL VL)L%L@ V; R^^^! L/&]!LLJ LL!LL VLfL!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR_ ^L/*LLLJ LL!LL VLf1L!LL VL)L%L@ V-LL)L=VLVR^K^^L!LL VL!f)L!LL VL)L%L@ V&LLVR^^^! L/&]R_ ^^L!LL VLgOL!LL VQL!g8L!LL VL)L%LL VLg)L%LL VQLf)L%LL VQQL-L%LL VLLL@ Vj  Ll^%LL! f LLJ fR_W ^L/*Lj  Ll^%LL! f LLJ fR_& ^L/*Lj  Ll^%LL! f LLJ fR_ ^L/*L0 L-LL-L%zLLP VLL)LQ L=VLVR_ ^^^^ ^^^^L!LL VLgL!LL VQL%gL!LL VL)L%L@ Vj Ll^%LL! f )LLJ fR_9 ^L/*L-L!LL VLgI-L!LL VQLg/-L!LL VLL%L@ Vj  Ll^%LL! f LLJ fR_ ^L/*LLLJ LL!LL VLfL!LL VQLfyL!LL VL)L%L@ Vj  Ll^%LL! f )LLJ fR^j^L/*LLL)LN L> VLL)LQ L=VLVR^=^^^^L-LA L> VLLLQ L=VLV^ ! L/&]R_^R^^R^^^^^L!LL VLgL!LL VQLgL!LL VL)L%L@ Vj Ll^%LL! f )LLJ fR_f^L/*Lj Ll^%LL! f -LLJ fR_5^L/*LL!LL VLfL!LL VQLL!LL VLL%L@ Vj  L l^%LL! f LLJ fR_^L/*Lj  Ll^%LL! f LLJ fR_^L/*Lj  Ll^%LL! f LLJ fR_l^L/*LLLL-LL, VLL)LQ L=VLVR _>^R^^^^^L!LL VLg"L!LL VQLg L!LL VL)L%L@ Vj L l^%LL! f )LLJ fR_^L/*L-L!LL VLf-L!LL VQLf-L!LL VQQLL!LL VLL%L@ VjL L Vl^%LL! f LLJ fR_8^L/*LLLLL-L$ VLL)L=VLVR_^R^^R^^^^^L!LL VLfL!LL VQL fL!LL VL)L%LL VLf)L%LL VQLf~)L%LL VQQL-L%LL VLLL@ Vj  Ll^%LL! f LLJ fR_C^L/*L)LL- VLL-L=VLVR_$^^^^ ^^^^L!LL VLgeL!LL VQLgNL!LL VL)L%L@ Vj Ll^%LL! f )LLJ fR_^L/*L-L!LL VLf-L!LL VLL%L@ Vj  L l^%LL! f LLJ fR_G^L/*LLLJ LL!LL VLf9L!LL VL)L%L@ VLL VLL)L=VLVR^K^^L!LL VL!f)L!LL VL)L%L@ V&LLVR^^^! L/&]R_^R^^^^^L!LL VLgL!LL VQLgL!LL VL)L%L@ Vj L l^%LL! f )LLJ fR_*^L/*L-L!LL VLg>-L!LL VQLg$-L!LL VLL%LL VLfL%LL VLLL@ Vj  L l^%LL! f LLJ fR_^L/*LLLJ LL!LL VLfKL!LL VL)L%L@ VLL=VLL) LLI VLL)L=VLVR^K^^L!LL VL!f)L!LL VL)L%L@ V&LLVR^^^! L/&]R_^R^^R^^R^^^^^L!LL VLgwL!LL VQL g`L!LL VL)L%L@ Vj Ll^%LL! f )LLJ fR_b^L/*L-L!LL VLf-L!LL VLL%L@ Vj  L l^%LL! f LLJ fR_ ^L/*LLLJ LL!LL VLfKL!LL VL)L%L@ VLL)L=VLL) L* VLL)L=VLVR^K^^L!LL VL!f)L!LL VL)L%L@ V&LLVR^^^! L/&]R^X^R^^^^^j Ll^$LL! f L-LJ f^^L/*LR^ ! L/&]!LLJ LL!LL VLfL!LL VQL)L!LL VL-L%L@ Vj L9l^%LL! f -LLJ fR_^L/*LLLJ LL!LL VLf;L!LL VL)L%L@ VLL=VLL. LLVR^K^^L!LL VL!f)L!LL VL)L%L@ VLLVR^^^! L/&]R_a^^L!LL VLfL!LL VQL)L!LL VL-L%L@ VLf ! L9 LLJ Lj  Ll^$LL! f L-LJ f^(^L/*LLLLVLLVR^ ! L/&]R_^^L!LL VLfL!LL VQLftL!LL VQQL)L!LL VL-L%L@ VjLL LVl^%LL! f -LLJ fR_*^L/*LLLVR_^^^^L!LL VLfCL!LL VQL)L!LL VL-L%L@ VL)L LLVR^^^L!LL VL fL!LL VL)L%L@ Vj Ll^%LL! f )LLJ fR^c^L/*L-L!LL VL f:-L!LL VLL%L@ V)LL=VL)L VLLVR^^R^ ^^! L/&]!LQ LLF LLLbLbE^^QL)UL9L0 L-L-L%zL' VL9LQ LL=VLVR^{^DQL)UL-*L)L)LL)L# VL9LQ LL=VLVR^;^9L0 LL; L%zL' VL9LQ LLQ L=VL*LLJ LL!LL VLfL!LL VQLfL!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR_ ^L/*LLLLQ LL=VLL-L VLLVR_^^^^L!LL VLg L!LL VQLfL!LL VL)L%L@ Vj Ll^%LL! f )LLJ fR_d^L/*LLLJ LL!LL VLfCL!LL VL)L%L@ VLQ LL=VLLL& VLLVR^K^^L!LL VL!f)L!LL VL)L%L@ VLLVR^^^! L/&]R_^^^^L!LL VLgL!LL VL)L%L@ V-LLJ LL!LL VLflL!LL VQLfUL!LL VQQL)L!LL VL-L%L@ VLQ LL=VLL-L1 VLLVR_^^^^L!LL VL fL!LL VL)L%L@ Vj  Ll^%LL! f )LLJ fR^^L/*LLLJ LL!LL VL fCL!LL VL)L%L@ V LQ LL=VL LLO VL LVR^K^^L!LL VL!f)L!LL VL)L%L@ VLLVR^^^! L/&]R^^^! L/&]R^^^L!LL VLfnL!LL VQL)L!LL VL-L%L@ Vj Ll^%LL! f -LLJ fR^^L/*L)LL)LVR^s^^j Ll^$LL! f L-LJ f^6^L/*LLLL=LLLv vaL쭥R^-^ ! L/&])rLLJ LL!LL VLg9L!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR_^L/*LLLLL!fQ^^^ ^ ^^ ^=^p^^QL0 L L; L%zLL3 VL LLL=VLV^^vQL0 L L; L%zLL3 VL LLL=VLV^S^AQQLQUL0 L L-L%zLL3 VL L LL=VLVR^^^^x LLE VR^/^^L7 L LLV^ ! L/&]1rLLJ LL!LL VLfZL!LL VQLfCL!LL VQQL)L!LL VL-L%L@ VLB L LL9VR^^^^^L!LL VLfL!LL VQLfjL!LL VQQL)L!LL VL-L%L@ VjLL L=Vl^%LL! f -LLJ fR^ ^L/*LR^^^^^! L/&]!LLJ LL!LL VLgL!LL VQLgL!LL VQQL)L!LL VL-L%L@ VLLJ LL!LL VLfL!LL VQLfL!LL VL)L%L@ Vj  L9l^%LL! f )LLJ fR^^L/*Lj  L=l^%LL! f -LLJ fR^^L/*L0 L)LL)L%zL4 R^^^^^jLLB L L LVl^$LL! f L-LJ f^X^L/*Lj  Ll^%LL! f )LLJ fR^'^L/*L0 L)L)L%zL) R^! L/&]R^S^^^^j Ll^$LL! f L-LJ f^^L/*LL) R^ ! L/&]!LLJ LL!LL VLfL!LL VQLfL!LL VQQL)L!LL VL-L%LL VLf-L%LL VQLf-L%LL VLLL@ Vj  L9l^%LL! f LLJ fR^^L/*Lj  L=l^%LL! f LLJ fR^^L/*L0 L)LL)L%zR^^R^^R^^^^^L!LL VL%fSL!LL VL)L%L@ Vj L=l^%LL! f )LLJ fR^ ^L/*LR^^^; ^ ! L/&]!LLJ LL!LL VLf7L!LL VQLf!L!LL VL)L%L@ V; R_^^^^L!LL VLfL!LL VL)L%LL VLf)L%LL VQLf)L%LL VQQL-L%LL VLLL@ Vj  L9l^%LL! f LLJ fR^n^L/*Lj  L=l^%LL! f LLJ fR^=^L/*L0 L)LL)L%zR^%^^^^^^! L/&]!LLJ Lj L9l^$LL! f L-LJ f^S^L/*Lj L=l^%LL! f )LLJ fR^^L/*L0 L)L)L%zR^yL!LL VL%fSL!LL VL)L%L@ Vj L=l^%LL! f )LLJ fR^ ^L/*LR^^^; ^ ! L/&]!LLJ LL!LL VLfWL!LL VQLf@L!LL VQQL)L!LL VL-L%L@ VL= LL9VR^˥^^^^L!LL VLfqL!LL VQL)L!LL VL-L%L@ Vj L=l^%LL! f -LLJ fR^\^L/*LLH LL9VR^G^^L!LL VLf!L!LL VL)L%L@ V; R^^^! L/&]!LLJ LL!LL VLf^L!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR^^L/*L0 LL)L%zR^楥^^L!LL VLfL!LL VQLfoL!LL VL)L%L@ Vj L=l^%LL! f )LLJ fR^w^L/*LLLL)L( VLLVR^X^^^^L!LL VLf,L!LL VL)L%L@ V0 LL; L%zR^^^! L/&])rLLJ LL!LL VLfxL!LL VQLfbL!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR^3^L/*LLLLN R^!^^^^A ^ ! L/&]!LLJ LL!LL VLfOL!LL VQLf8L!LL VQQL f L!LL VL)L%L@ VR^^^ ^^^^^ ! L/&]!LLJ LL!LL VLfNL!LL VQLf7L!LL VQQL)L!LL VL-L%L@ VLN R^^^^^A ^ ! L/&]!LLJ Lj L9l^$LL! f L-LJ f^S^L/*Lj L=l^%LL! f )LLJ fR^(^L/*L0 L)L)L%zR^; ^ ! L/&]!LLJ LL!LL VLfRL!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR^ ^L/*LR^^^; ^ ! L/&]!LLJ LL!LL VLf`L!LL VQLfIL!LL VQQL)L!LL VL-L%L@ VA L; L-L VLLL9VR_^^^^L!LL VLg!L!LL VQLg L!LL VQQL)L!LL VL-L%LL VLf-L%LL VLLL@ Vj  L=l^%LL! f LLJ fR_E^L/*LL!LL VLfpL!LL VQLfVL!LL VQQLL!LL VLL%L@ VA L0 LLL%zL-L VLLL9VR_^R^^R^^R^^^^^L!LL VLfL!LL VL)L%LL VLf])L%LL VQLfC)L%LL VQQL-L%LL VLLL@ VLS LLL9VR_,^^^^^^L!LL VLfL!LL VQL)L!LL VL-L%L@ Vj Ll^%LL! f -LLJ fR^^L/*LLLjL LVl^%LL! f LLJ fR^y^L/*LLLL!LL VLf>L!LL VLL%L@ V0 LLL%zLZ L)L L9VR ^'^R^^^! L/&]!LLJ Lj L9l^$LL! f L-LJ f^q^L/*LLLjL L=Vl^%LL! f LLJ fR^7^L/*LLL0 LL-L%zLLVR^! L/&])rLLJ LL!LL VLfUL!LL VL)L%L@ VjLL9Vl^%LL! f )LLJ fR^%^L/*LR^^^-L; ^ ! L/&])rLLJ LL!LL VL flL!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR_^L/*LLLL)L" VLL=VR_^^L!LL VLfeL!LL VQLfNL!LL VQQL)L!LL VL-L%L@ VLN L; L-L VLLLVR_Y^^^^L!LL VLg&L!LL VQLgL!LL VQQL)L!LL VL-L%LL VLf-L%LL VLLL@ Vj  Ll^%LL! f LLJ fR^^L/*LL!LL VLfuL!LL VQLf[L!LL VQQLL!LL VLL%L@ VLN L0 LLL%zL-L VLLLVR^<^R^^R^^R^^^^^-L^ ! L/&]1rLLJ LL!LL VLfL!LL VQLfL!LL VQQL)L!LL VL-L%LL VLf`-L%LL VLLL@ Vj  L9l^%LL! f LLJ fR^:^L/*L0 LL)L%zR^'^R^^^^^; ^ ! L/&]!LLJ LL!LL VLf~L!LL VL)L%LL VLfY)L%LL VQLf?)L%LL VQQL-L%LL VLLL@ V0 L)L; L%zR^˥^^^^^^L!LL VLfL!LL VQL)L!LL VL-L%L@ Vj L9l^%LL! f -LLJ fR^R^L/*LL!LL VLf!L!LL VLL%L@ VR^#^R^ ^^; ^ ! L/&]!LLJ LL!LL VLfL!LL VL)L%LL VLf)L%LL VQLfr)L%LL VQQL-L%LL VLLL@ Vj  L9l^%LL! f LLJ fR^6^L/*L0 L-L)L%zR^#^^^^^^! L/&]!LLJ LL!LL VLfRL!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR^ ^L/*LR^^^; ^ ! L/&]!LLJ LL!LL VLfL!LL VL)L%L@ V-LLJ LjL L9Vl^$LL! f L-LJ f^*^L/*LLLLM LL=VR^^j  Ll^$LL! f L-LJ f^*^L/*LLLL LL=VR^! L/&]R^^^L!LL VLf~L!LL VQLfhL!LL VL)L%L@ Vj Ll^%LL! f )LLJ fR^B^L/*LLLL8 LL=VR^)^^^^U LL=V^ ! L/&])rLLJ LL!LL VLf(L!LL VL)L%L@ V; LL9VR_;^^L!LL VLfL!LL VQLfmL!LL VQQL fUL!LL VL)L%L@ VjLL=Vl^%LL! f )LLJ fR_^L/*LR_^^ ^^^^L!LL VL%fUL!LL VL)L%L@ VjLL=Vl^%LL! f )LLJ fR_I^L/*LR_B^^L!LL VLgL!LL VQLgL!LL VQQL)L!LL VL-L%LL VLf-L%LL VQLf-L%LL VLLL@ Vj  Ll^%LL! f LLJ fR^^L/*LLLjL L=Vl^%LL! f LLJ fR^Y^L/*LLL0 L LL zL-L%zLL9VR ^1^R^^R^^^^^! L/&])rLLJ LL!LL VLf(L!LL VL)L%L@ V; LL9VR_V^^L!LL VL%fSL!LL VL)L%L@ Vj L=l^%LL! f )LLJ fR^^L/*LR^^^L!LL VLfL!LL VQLfL!LL VQQL)L!LL VL-L%L@ Vj Ll^%LL! f -LLJ fR^u^L/*Lj  L=l^%LL! f LLJ fR^D^L/*LLL0 LLL-L%zLL9VR^^^^^! L/&]!LLJ LL!LL VL fSL!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR^Q^L/*LR^J^^j L9l^$LL! f L-LJ f^^L/*LR^ ! L/&]!LLJ Lj L9l^$LL! f L-LJ f_h^L/*Lj L=l^%LL! f )LLJ fR_=^L/*Lj Ll^%LL! f -LLJ fR_ ^L/*LL!LL VL fL!LL VLL%L@ Vj  Ll^%LL! f LLJ fR^^L/*Lj  Ll^%LL! f LLJ fR^^L/*L-L)LL%fUL!fQL^^^^^-L) LLVL0 LLL%zzL0 LL-L%zR^#^R^; ^ ! L/&]!LLJ LL!LL VL fL!LL VL)L%L@ Vj L9l^%LL! f )LLJ fR^_^L/*Lj L=l^%LL! f -LLJ fR^.^L/*L0 L)L)L%zR^^^; ^ ! L/&]!LLJ Lj L9l^$LL! f L-LJ f_Q^L/*LLLLLJ LL!LL VLfL!LL VQLfL!LL VQQL fL!LL VL)L%LL VLfj)L%LL VQLfP)L%LL VQQL-L%LL VLLL@ VLLLV VLL)L=VLVR_y^^^^^^ ^^^^L!LL VLfL!LL VQLfL!LL VL)L%L@ Vj  Ll^%LL! f )LLJ fR^^L/*LLL; LLL-L=VL0 LL L0 LLL; L%zL%zL< LN L: VLL)L=VLVR^^^^^j  Ll^$LL! f L-LJ f^W^L/*LLLL!b L%b^,LLV^ QLLLL VLLV^R^ ! L/&]R^! L/&]!LLJ LL!LL VLfL!LL VQL)L!LL VL-L%L@ Vj L9l^%LL! f -LLJ fR_^L/*LL!LL VLf6L!LL VLL%L@ VL< L-L)L=VLVR_X^R^ ^^L!LL VLgoL!LL VL)L%LL VLgJ)L%LL VQLg0)L%LL VQQL-L%LL VLLLL VLfLLL VQLfLLL VLLL@ Vj  Ll^%LL! f LLJ fR_^L/*Lj  Ll^%LL! f LLJ fR_N^L/*LL!LL VLfJL!LL VLL%L@ V0 L-LL-L%zL!L% VLL)L=VLVR_^R^*^R^^R^^^^^^^L!LL VLfL!LL VQLfL!LL VQQL)L!LL VL-L%L@ VjLL LVl^%LL! f -LLJ fR_H^L/*LLL)QLjL LVl^%LL! f LLJ fR_ ^L/*LLLLL-L: VLL)L=VLVR _^^^^L!LL VLfUL!LL VQLf>L!LL VQQL)L!LL VL-L%L@ VLK LLVR_l^^^^L!LL VLf L)L "^2 L)L "^& L)L "^ L)L "^ L)L "^9LL!z9LLzLL 9L)L)LS_ zrrL^ ^!^#^%^4^5QL LL L *^ ^ ^QL LL *^ ^L)L L VL r9LL L)L #L LrL LLj9LL VL l^4LLf L aLw f)L L ^ ^L/*L="LL L L L L9LL L L L9LL L L9L L L L L L9L L9L L9L L9L L9LL L9LL L9L L9L L9L L9L L9L L9L9L LL L L9LL L L9L LL LL j L9l^7LLf L aLUDf L-L VL ^ ^L/*)L L)LL VL)L L L RL LL LL j L9l^7LLf L aLUDf L-L VL ^ ^L/*)L L)LL VL)L L RL L L L9L L L L9L L L L9L'L L L9L\L L L9L"L L L9LL L%L VL L L9L LLL VLL LLf)LL L9V)LL L L L=*RL LL LLL%L VL f^L-L-L!LLL VL L92RL L9L L9L L9LL L L L9L LLL!b L%b^$)L L9*^QL-LL L=2^E 7 7 |7  7 U7 F7 L/`S7 _KL^^^#^-^7^A^J L)Lt "^> L)Lt "^2 L)Lt "^& L)Lt "^ L)Lt "^ L)Lt "^9LL!z9LLzLLt 9L)L)L_:zrrL^^T^V^X^Z^i^jQLL L f L Lf LLk V^A^^QL LL Lk *^# ^ ^ ^QL LLk *^ ^L)L L VLo r9LLs L)L #L LrLL^ Lj Lc L L9LL^ Ld L L9L^ LLj9LLu VLP l^4LLf Ll aLw f)L~ L ^ ^L/*L="L9L L9L L9L zLLt 9LLI"W3zLQ L9Lv L9L L9LR L9Le L9LT L9Lf L9L` L9LU L9Lw L9L9L L^ LL%L)L_ L%Lx VL)LLV L L9*RLX L L9L L L9Lg L L9L L L9L9L LL LLh j L9l^7LLf Ll aLUDf L-L VLo ^ ^L/*)L L)LLW VL)L LZ L RL LL LLh j L9l^7LLf Ll aLUDf L-L VLo ^ ^L/*)L L)LLW VL)L L RL^ LL_ LLL%L| VL f^L-L-L!LLLx VL L92RLL^ Lr L9LL^ Lr L9LLr L9LLr L9LY LLL!b L%b^$)L L9*^QL-LL L=2^L LL^ L} VL9L LL^ L} VL9 Lo L L"L VL9L L\L VL9L L L VL9L L L VL9L L L VL9L^ L%LLx VLj LLfLL L9VL LLa L VL=L L9L LL^ L} VL9 Lo L Lo L L#-7 I #*Vc7 K #47 L #(07 K #ǖA7 K #q&7 I #+7 I #H>7 G #f7  LL 7 F L ?6L%7 F L ?T'qL7 F L ?UL7 F L ?L 7 F L ?E@7  #f7  #':7  #7  LL 7  L ?6L%7  L ?T'qL7  L ?UL 7  L ?E@ L? L LKL@ L L LMLA L} L LKLILJLB L LMLL LOL:L LQL;L LSL8L LUL7L LWL9L L\LYL L_LC LZL L%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL%zL LLD vL VE L-?+E L?WE L?T8E L?i)E L?Ɇz!E L?lrE L ?NE L ?SE L ?Fq:E L?ǚE L ?^_E L ?jŠE L?By7E L? E L?)E L)?_zpE L?3RE E 7  7 U7 7  #,7  #' 7  #7  #N+7  #7  #*7  #)7  #%7  #7!  #D\7"  ##o7#  #]7$  #617%  #K97&  #7'  #iT++7(  #7)  #,7*  #"7>7+  #}7,  #M7-  #7.  #(O97/  #!|70  #5`*71  #772  #̨g73  #@\ 74  #7.75  #iW76  #ϱp77  # ^78  #&]79  #%7:  #MR7;  #'7<  #s&7=  #47>  #K7?  #'}7@  #>37A  #в7B  #-7C  #F7D  #F7E  #M7F  #+7G  #(7H  #-7I  #!7J  #37K  #>97L  #7M  #O177N  #\7O  #O-7P  #]U7Q  #v7R  #17S  #17T  #GA7U  # K17V  #=.#7W  #47X  #+7Y  #7Z ] LL_ 7^  L^ ? L?s L? / L? L?) L?t  L?  L?hz L?R< L ?\ L?W  L$? L? _7 L6? L+?J L4?d{>; L?: L?#6 L?0) L?B L?< L-?O L ? L ?ү L?u L?O L ?W L ?Pw L?3U= L?y L ? L!?_Es L?~ L,?^_ L1?% L0?sI L/??/; L2?W" L.?o  L?$\ L?i5D; L)?: L(?}  L&?Yo; L'? L*?5 L7?  L5?) L3?b L%?D.' L"?Mu4R:  7f 7g7ha#^7i\#!07jd# 7k\#n47l]#%7m\#7nZ#cV7o\#|7p\#^#7q\#!!7rd#7sh#-X7t]#617uh#-q7vb# >7w]#]7x\#S)7y]#iT++7zh#a17{]#7|b#f7}Z# 7~\#Sp7\#7h#S7]#77h#7.7\#:%7h#iW7h#MR7]#%7\# r7\#ȩ7\#77Z#7Z#7\#Q?7\#G 77]#/7h#K7\#Eg7\#N?7]#в7\#77_#9V7h#F7\#B%7\#. 7\#,7\#M 7e#e&7_#n7Y#D7d#N7g#f7Z#\7\#Ee7Y#!I7]#v7e#:7[#Edž47\#k \7\#{7\#U67]#GA7\#o7\#Y4$7]#=.#7Y#f7\#Q77\#?7c#ſ7]#7\#17\#JEA 7e#!I7\#7Z#ſ7c#!I7c#9л7\#7\#'7\#F$7Z#9л7]#N+7]#*7]#7Y#?7\#Z(7Z#!I7h#e7[#ڥA7\#^_7\#V7Z#?7]#K97]#7h#7]#7\#+7_#C<7\#}7\#.7h#!|7Z#Z7h#&]7\#7]#%7Z#bS7]#s&7]#'7c#:7Y#ſ7\#"7\#7Y#9л7h#>37\#d 7\#K77h#M7Y#4H(+7f#3U=7\#77\#7\#w7\#.+7]#37\#E7e#\i7\#QK,7e#ſ7h#O177a#;7\#7u7]#O-7]#1m7\##{!7b#17]#17\#j 7]# K17\#.7\#; 7\#7h#(07Z#n7e#?7\#]U7Z# S7 LL 7 XL ?L 7 XL ?+L 7 XL ?(8L 7 XL ?L 7 XL ?ĘlL 7 XL ?& LL 7 XL ?;WLLL aL L L LLL L L L! L" L# L$ LLL% vLLL( vLL)L+ vLL-L. vL L0 LLLL)L1 eLL2 aL L3 vL L4 L5 LLLLL-LL6 -2)L-LLLLLL: v)2L)L LLL@ veL L L-LA vaF LL!7G XLG ?Ai3L%7H XLH ?nZL7I XLI ?NL7J XLJ ?-)3K LLLLS vLL)LU vLLLLLLX vaLLLZ vLL\ LLLLLLLLLLLLLb v2-L LLLLLLL&LL LLLLLLL v-2)LLL LLL L v)2LLLLLLLLL#L L$L v eL)L LLL LL LL va LL LL LLLL vLLL vL L) L"?׹ L?e& L? [ L?C]& L ?e2 L?1 L? L?J L?V L?\م L?' L?ɞ L?v L ?# L ? L? L$?"ݏ L? L ?% L?i0 L-?Q L?2m' L?4 L!? L)?ܖ2 L? L? 7 L?K L?;M L?C L?Zs L?uZ1 L?G" L?xc8 L&?N  L%?  L#?) L?w L ?; L ?gR)  77|777U77L/`S7M _#L^ ^^^&[ L)L "^\ L)L "^, L)L "^9LL!z9LL%z9LLzLL 9L)L)L (I/zrrL^ ^^$^7QLe LL ^%QLf L숥^QL7 LL( L^LL5 VL% rLLLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLbLcLlcLcLcLcLcLcLcLc__ ___^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^z^x^r^p^j^h^b^`^Z!^X^R!^P^J%^H^B%^@^:^8^2^0^*^(^"^ ^^^^^ ^^L)rLLLLL fiQLUL)*L)L9LL9f>LL-L=VL-LL-L VLL L-L L VLVR^9^R^ ^^LLL VLL LL L VLJRrLL Lj L9l^$LL f L-L f^S^L/*Lj L=l^%LL f )LL fR^^L/*L L)L)L%zR^L!L VL%fSL!L VL)L%L Vj L=l^%LL f )LL fR^L^L/*LR^E^^L!L VL!f!L!L VL)L%L V) R^^^ L/&]!9LL LL!L VL fL!L VQLf~L!L VL)L%L Vj 9L=l^%LL f )LL fR^^L/*LLL L VLLL-L L VL9LVR^z^^^^L!L VL%f&L!L VL)L%L VLR^=^^LL L VLLL L VL9LV^  L/&]!LL LL!L VL fL!L VQLfL!L VQQL)L!L VL-L!L VQLL%L VLL LL!L VL fLL!L VQLf6L!L VL)L%L VL LL)L VL9VR^.^^^^-L-L LL=V^  L/&]R_ ^^^^L!L VL fCL!L VQL)L!L VL-L%L VL)L LL=VR_ ^^L!L VLfL!L VL)L%L Vj Ll^%LL f )LL fR_@ ^L/*LLL LL!L VLf6L!L VL)L%L VLL VLLL=VR^P^^L!L VL!f.L!L VL)L%L V&L0 LLVR^^^ L/&]R_ ^^L!L VLfL!L VL)L%L Vj Ll^%LL f )LL fR_B ^L/*LLL LL!L VLf;L!L VL)L%L VLL VLL LL=VR^P^^L!L VL!f.L!L VL)L%L VL0 LLVR^^^ L/&]R_ ^^L!L VL fL!L VQL!f{L!L VL)L%L VjLLVl^%LL f )LL fR_) ^L/*LLL-LL VL)L LL=VR_ ^^^^L!L VL fL!L VQL%fL!L VL)L%L Vj Ll^%LL f )LL fR_ ^L/*Lj Ll^%LL f -LL fR_X ^L/*L)LL L VL)L)L L VLL=VR_+ ^^^^L!L VL g L!L VQLfL!L VL)L%L Vj Ll^%LL f )LL fR_^L/*L-L!L VL f-L!L VQL%fy-L!L VLL%L Vj  Ll^%LL f LL fR_H^L/*L-LL L VLLL L VLL=VR_^R^^R^^^^^L!L VL g(L!L VQL gL!L VL)L%L Vj Ll^%LL f )LL fR_^L/*L-L!L VLf-L!L VLL%L Vj  Ll^%LL f LL fR_?^L/*LLLL!L VLfBL!L VLL%L VLL VLLLL VLL=VR_^R^^R^^^^^L!L VL fL!L VQLfL!L VL)L%L Vj Ll^%LL f )LL fR__^L/*Lj Ll^%LL f -LL fR_.^L/*LLLLLLLL=LL> vaL쭥R_^^^^L!L VL gjL!L VQLgSL!L VL)L%L VLg.)L%L VL-LL Vj Ll^%LL f -LL fR_h^L/*LLL LL!L VLfvL!L VL)L%L Vj  Ll^%LL f )LL fR^|^L/*LLL L VLL)L VLL=VR^R^^L!L VL!f.L!L VL)L%L VL0 LLVR^^^ L/&]R_^^ ^^^^L!L VL fL!L VQLfL!L VL)L%L V-LL Lj  Ll^$LL f L-L f^8^L/*L-LL L VLL L LL=VR^RL!L VL%f4L!L VL)L%L V-L L LL=VR^^^ L/&]R_^^^^L!L VL fL!L VQLfL!L VL)L%L V-LL Lj  Ll^$LL f L-L f^8^L/*L-LL L VLL L* LL=VR^RL!L VL%f4L!L VL)L%L V-L L* LL=VR^^^ L/&]R_^^^^L!L VL fEL!L VQLf.L!L VL)L%L VL LL=VR_/^^^^L!L VL g_L!L VQL gHL!L VL)L%L Vj Ll^%LL f )LL fR_^L/*L-L!L VL f-L!L VQL f-L!L VLL%L VL fL%L VQLfL%L VQQLL%L VLLL Vj  Ll^%LL f LL fR_^L/*LLL L VLLL-L VLL=VR^ᥥ^R^"^R^^R^^R^^^^^L!L VL fL!L VQLfL!L VL)L%L Vj Ll^%LL f )LL fR^N^L/*LLL L VLL-L!L L L-LVL R^^^^^ L/&]!LL LL!L VLfL!L VL)L%L VL fp)L%L VQLfV)L%L VQQL-L%L VLLL VL LL VLL-L" VLL9VR_^^^^^^L!L VLfL!L VL)L%L Vj L=l^%LL f )LL fR_P^L/*LLL LL!L VLfCL!L VL)L%L VL LL VLLL& VLL9VR^P^^L!L VL!f.L!L VL)L%L VL0 LLVR^^^ L/&]R_^^L!L VL fL!L VL)L%L Vj Ll^%LL f )LL fR_F^L/*LLL LL!L VL fCL!L VL)L%L VL LL VLLL VLL9VR^P^^L!L VL!f.L!L VL)L%L VL0 LLVR^^^ L/&]R^^^L!L VL fnL!L VQL)L!L VL-L%L Vj Ll^%LL f -LL fR^.^L/*L)LL)LVR^^^-^  L/&])rLL LL!L VL g2L!L VQLgL!L VQQL)L!L VL-L%L VLL LL!L VLfL!L VL)L%L Vj  L9l^%LL f )LL fR_^L/*Lj  L=l^%LL f -LL fR_[^L/*L L)LL)L%zL R_>^^L!L VL fL!L VQLfvL!L VL)L%L Vj  Ll^%LL f )LL fR^^L/*L LL-L VLL L)L%zL R^^^^^jLL L L LVl^$LL f L-L f^X^L/*Lj  Ll^%LL f )LL fR^'^L/*L L)L)L%zL R^ L/&]R^S^^^^j Ll^$LL f L-L f^^L/*LL R^  L/&]!LL Lj L9l^$LL f L-L f^S^L/*Lj L=l^%LL f )LL fR^^L/*L L)L)L%zR^yL!L VL%fSL!L VL)L%L Vj L=l^%LL f )LL fR^ ^L/*LR^^^) ^  L/&]!LL LL!L VL fL!L VQLfL!L VQQL)L!L VL-L%L VLf-L%L VLLL Vj  L9l^%LL f LL fR^^L/*Lj  L=l^%LL f LL fR^^L/*L L)LL)L%zR^^R^^^^^L!L VLfSL!L VL)L%L Vj L=l^%LL f )LL fR^ ^L/*LR^^^) ^  L/&]!LL Lj L9l^$LL f L-L f^^L/*L)L!L VLf)L!L VL-L%L Vj L9l^%LL f -LL fR_^L/*Lj  L=l^%LL f LL fR_d^L/*LLLL LLLL%zR_B^^L!L VL g L!L VQL fL!L VL)L%L VLf)L%L VL-LL Vj L9l^%LL f -LL fR^^L/*Lj  L=l^%LL f LL fR^z^L/*LLLLLL!b L%b^&L L^ L L. LLV^R^0^^ ^^^^ L) ^  L/&]!LL LL!L VL fL!L VQLfqL!L VQQL)L!L VL-L%L Vj L9l^%LL f -LL fR^^L/*L L-L)L%zR^^^^^L!L VLfSL!L VL)L%L Vj L9l^%LL f )LL fR^ ^L/*LR^^^) ^  L/&]!LL Lj L9l^$LL f L-L f^S^L/*Lj L=l^%LL f )LL fR^(^L/*L L)L)L%zR^) ^  L/&]!LL LL!L VLfRL!L VL)L%L Vj L9l^%LL f )LL fR^ ^L/*LR^^^) ^  L/&]!LL LL!L VL gL!L VQLgwL!L VQQL)L!L VL-L%L VLL LL!L VL fL!L VQLfL!L VL)L%L Vj  L9l^%LL f )LL fR^^L/*LjL L L=Vl^%LL f -LL fR^^L/*LLLL LL L LL%zR^v^^^^jL L=Vl^$LL f L-L f^3^L/*LLLL L LLL%zR^ L/&]R^^^^^ L/&])rLL LL!L VLfUL!L VL)L%L VjLL9Vl^%LL f )LL fR^%^L/*LR^^^-L) ^  L/&])r9L' L+ VLL LLLbLb^,^(QLL=R^#^QLL=R^^L)쥥 L! LLL-L)LLJ vajL L9L LL l^CLLf L# aL65f!)L L. L-L$ L=V^ ^L/*)RL LL L LL-L-LLQL VL9)RrM 7 7 L/`S7 _ L LX "rLT LL!f$LZ L)LLb VL-LLq VR^L)L} L)Lj "L!f ^()L)L{ LLLLL%L9VL%zrLT L ^^^ ^$^(^6^D^Q^^^k^x^v[ ^^pu ^z^jz ^t^dw ^n^^LT Lv ^^^NL^ Lv ^N^>L9Lx ^?^/L9L_ ^0^ L9LY ^!^L9Lm ^^L LL !LT L!^F^J^N^R^V^Z^^^b^f^j^n^r^v^z^~^^^^^^^^^^^^^^^^^^^^^^^^l^^^^^^^^^^^^^^^^^^^^^^z^^t^~^n^x^h^r^b^l^\^f^V^`^P^Z^J^T^D^N^>^H^8^B^2^<^,^6^&^0^ ^*^^$^^^^^^^L LL !L9LL=LL9LL9LL)RL9L)L9L L-L=VL-LL)L)L)L` CRrLT L!fV ^L9Lp 9L=L9LLL)RLT LLf37V Q #f7W S #F7X R #*=7Y O #p>7Z R #n77[ R #N+7\ S #M7] O #97^ R #7_ R #V/7` R #}7a O #VK7b R #7c R #!;7d R #L;7e R #7f O #p>7g R #^7h R # 7i Q #N7j R ##o7k R #oc|7l R #в7m R #7n R #ط7o S #O177p Q #!I7q R #\7r R #,7s R #47t R #|7u R #77v R #~277w R #|57x R #?7y R #37z S #!|7{ R #5`*7| Q #7} R #K97~ S #MR7 R #:z7 S #)7  L L LLLL aL L LLLLL-LL)L v-2)LLLL v)2LL eLL)LLLLLLLL v aLL vL L?y L?f0  L?Ȃ L?*x L ?f L?n# L-?e# L)?  L?0  L?3U= L? R   7 U7 L/`S7 _\LL 9LL3*z LL "rjL(L VLLL VLLL!bL%b^>^: L#L V^^*UL!fQLL L R^d^^^LL LLL!b L%b^5 L'L V^)QL)ULL LL)L L V^LLLL QL ULzRl^*LLf L aLw f)^ ^L/*!r L 9L LL%fUUL%fGUUL!f7QLUQL=L=L-LVL=L-LVLR^^^ ^^^^LjL L l^LLf L a ^L/*LLLL!b L%b ^ ^QL9LL=VL ^L)LL VLL L +rL)L L +rL9L)L +r9LL L=VLLL VL LLL bLb^^)L LL%fLUL%f>UUL!f.QLUQL9LL=VL9L-L=VR^^^ ^^^^LLL L)LL%z^i^9L L VL!fLL9LL L=VL ^2^L L VL!fLL9L-L=VL  L)L)L9VL)LL=LL vL-L LLL bL bL bLcL cL(cL cL c+L cLcL cL cL cL cL4cL cL cLckL<cL cL cL cLcL@c__LL VL LLL L __LL VLLL L __hLL VLLL L L _W_@LL VLL!L VL$f(LL%L-L L%L VL L ^LLLbLb(Lb3Lb>^N^JL L ^M^8L L ^;^&L L ^)^L L ^^L)L L __u-LL=LL L VL _g_P-LLL L=VL _G_0LL VLLLL L=VL)L V__L LLL!b L%b ^?L^7QL)ULLL-L=VLL=L-L VL V^__ L-__-LL=L vLL L VL __i L-_s_\ L-_f_OL LL%fUL%fUULL!bL%bA^^QL)UQLLL-L=VLL-L=VL L VR^~^hUUUL!fTQL)UQL-UUQLLLL=VLLL=VL LL=VL L VR^ ^^^^^^^L_z_cLL VLL L_\_ELL VLL L_>_'LL VLL VLLLLL L=VL V__j L L l^LLf L a ^L/*LLLLL!b L%b ^ ^QLLL=VL ^L __zj L L l^LLf L a ^L/*LLLLL!b L%b ^ ^QLLL=VL ^L __-L _^ L-_^-LL=L vLL L VL ^^LL VLLL ^^ L L LL=LLLLL vLL L VLLL!b L%b ^*L^"QLLLL LL V^R^?^(L LLVLLLL)R^^L LL L ARrL LL LL%f3QL!f%UL!fQL-LL9V^^^ ^^^^ L )rLLL L)LL L%zL-LLL!b L%b ^$ ^ QL L9L)L=VL L%z^L 3RLLL L)LL L%zL L9LL=VL L%zL 3RLL L L L9LL=VL L9LL=VL L%zL%zL 3RLLLL L LL^)_;_]____<_`_____r____W_{__-QLL ^^-^E^]^u^^^^^LL)L L ^LL)L L ^LL)L L ^LL)L L ^QLL LL L ^jQLL LL ^PQLL LL ^6LL)L L ^LL)L L ^_QLL )LL9L)L V_QLL()L LL-L9VL L%z_QLULL LL -L LLL9VL L%z_QLULL -L LLL9VLL9LL VL%z_RQLULL -L LLL9VL LLL9VL L%zL%z_QLL)LL9L vL)L V_*LL!b L%bJ^QL)ULL L LLL9VL L LL9VL L%zL%z^GQL)ULL L LL-L9VL L LL9VL L%zL%z^_DQLUL)*LL L LLL9VL L LL9VLLLL!b L%b ^$ ^ QL LL)L9VL L%z^L%zL%z_QLUL)*LL4L)L L LLL9VL L LL9VL L%zL%z_gQLULL LL-L VL -L LL-L9VL L%z_#QLUL)*LL L-L L LLL9VL L LL9VL L%zL%z_QLL )LLLL!b L%b ^$ ^ QL LL)L9VL L%z^_|QLL)LLLL!b L%b ^$ ^ QL LL)L9VL L%z^_,-L<_#QLULL -L LLL9VL LLL9VL L%zL%z^QLL )LL9L vL)L V^QLL -LL ^QLUL)*LLL9L vL)L VLL LLL9VLLLL!b L%b ^:^6QL LL L LLL9VL L%zL VLL%z^L%z^-Lf  LLLL(L L%z^(-Lf LL L(L L%z^ L)LLL!b L%b ^ ^QL LLL L%z^LL)L)L VLL kRr LL9 7 |7 U7 F7 7 L/`S7F_L^ ^^^)^2L)L"^&L)L"^L)L"^L)L"^9L-L-L-L!zrr9LL%z9LLz9LLzLL9LLT5z L #xBLL)L?xBL L #xBLL)L?xBLL/mLL/1&LL!LL-f.-L$L)LL)L/AVL)L/2L%e_ L)L #xBLL #Hg Lz?Hg  L?xBLRr #Hg L #xBLL L)QL-LL L# sLL L# sLV?xBL L)U?Hg R%LL?Hg L?xBLL&? L'?- L(?m[L)?m$Lj#LL+V#xBLL L# sLlrl^LLf L"L/*L/*!LL!f^^^LL%f^^^LLf^^^L^ ^^!^(^6*L9LL*^%1L^1L^QL9LL*^L^ ^^!^(^6*L9LL *^%3L^3L^QL9LL *^LL!fQL^^^5LLLLbL%b^0^,QL9LLVR^$^QL9LLVR^^7LLL!f+*LLLL8L)LVL ^^^7LLL L9L L)LL!f!UL)L:LLVL^^^;L)rLLGLL>L)LLRL^ ^^^^QLUL)*LL=LL?LLVLVLLVLL!fL@LV^0^^LLLVL9LLVLALL[^OQL-LL2^>QL-LBLV-LLV-LCL2^QL-L9LL2^rL LLLLL9VL)R7%%L? %L?xBL%L?m%L?- %L?Hg %L?m[F7 |7 7F7 77#K7 #ſ7 #9л7#lY7# 7 #57  #Z7 #17 #(07 #q7 #iW7#C7#!I7#f7#eK<7#a17 # S7LL7 L?"63L7 L?==L7 L? `L 7 L?;!LL#7" L"?;W L*7$,LL/HoL#g%Z7+-L.L/L0L)L2L-L4L6L9L<LLLLDaLELFL)?lYFL?UJFL ?3U=FL?1FL?JWFL? FL?+0FL?YsFL?2FL?RFL-?JFL ?~R FF7 7  #ſ7  #n7  # 7  #*=7  #N+7  #n77  #cV7  #7  #V/7  #7  #e7  #J7  #r!7  #7  #^7  ##o7  #,7  #47  #77  #|57  #"637  #37  # P7  #!|7  #7.7  #5`*7  #K97  #&]7  #MR7  #JW7  #7  # 7  #K7  #-7  #>37  #F7  #W7  #R7  #}7  #!;7  #L;7  #7  #N7  # 7  #7  #eK<7  #oc|7  #17  #в7  #7  #ط7  #O177  #\7  #3U=7  #|7  #~277  #?7  #17  #:z7  #(07  # S7  LL 7  L ?;W L LLL)L)LL vaL LLLL aL L L)? L?  L? L?f0  L?H L-?3U=R  7 7  7  7 7  #K7  #'}7  #-7  #>37  #nN 7  #7  #-7  #f0 7  #7  #N+7  #M7  #V/7  #}7  #!;7  #L;7  #7  #7  #!7  #7  #ڥA7  #"7>7  #^7  # 7  #f7  # >7  ##o7  #>97  #oc|7  #]U7  #7  #в7  #O177  #,7  #47  #\7  #f7  #77  #?7  #17  #f7  #!|7  #47!  #5`*7"  #7.7#  #iW7$  #&]7%  #K97&  #57'  #(07(  #MR7)  #:z7*  #+7+ - LL/ 7.  L. ?;M L?f0 M L)?3U=M L?yM L?~M L?r1]M L?9uM L?^_M L ?W"M L ?} M L ?5M L? M L?)M L ?D.'M L?Mu4RM M 7 7F77 77#47#^7#!I7#ſ7#7#n7# 7#*=7#N+7#n77#7#V/7#|7#cV7#7#i7#e7#ڥA7#^7# >7##o7#^_7#47#f7#,7#77#|57#}7#37#!|7#7.7#5`*7#e&7#iW7#^'X7#K97#MR7#b 7# r7#7#9W7#ſ7#7#K7#Eg7#-7#3U=7#9л7#>37#F7#. 7#M7#D7#}7#\PC37#!;7#}w7# 7#E7#f7#oc|7#в7#/7#ط7#O177#;7#\7#|7##{!7#!I7#h7#?7#17#T7#:7#o7#!v7#; 7#7#(07#?7#?7# S7#f7LL!7L?=L%7L?}YLLL L L LLLLL*LL%zLLLLLLaLLLLLLLaL LLLL L L LLLvaLLLLLLLLLLLLLL L LLL!LLL LL!LLL%v 2 L LL LLLLL&v2 LLL L'v2LLLLL,v2L.2LLL L LL2v2LL LLLLL?v2-LLLLLLCv-2)L L)LLLLLLL LL LLLL'L LL-LLLLJv)2L-LLLLLLMveL)LLOvaLLL LLVvLWL?MWL ?6WL?2WL?*WL? PWL ?eWL?By7WL?5_WL? gWL?dd,WL? WL ?WL ?g>WL?KWL?š9WL-?Z0WL?WL?#6WL ?3WL?PWL)? %WL?y(WL?l5WL?_WL?C4WL?IbWL?WL?WL?Mu4WL?m%WL?ݩ%RWW6 6 6L/`S7o_ZL}LNVLRLLL!b L%b ^<^8UL!fQLR^'^^QL)UL}LLRLP:^L}LNVLRLLL!b L%b ^#^UL!f ^^^QL^lLLaLNVLPVLLlLNVLRLLL!b L%b^bL8LQ2^ QL^/Ho#]tLIL#.Z!LSLULLfO^LMWYL[LlL]LHL#.Z!o7G|7H7IF7J7KI#9V7LK#O177MJ#n7NK#>37OJ# 7PK#F7QH#cV7RTL%L/HoL#g%Z7SVL%L/HoL#g%Z7UXL!L/HoL#g%Z7WZL!L/HoL#g%Z7Y\L%L/HoL#g%Z7[^L%L/HoL#g%Z7]_L`LcLLLdLeLfL%L/HoL#g%ZL/LL L/VL LL L)LdL/VzLgLhLL/HoL#g%ZLiLjL%L/HoL#g%ZLkLmLnLoL ?oL ?xoL?oL?oL?roL?=oL)?UoL?&oL?O&oL ?oL?oL??oL-?z_(?oL ?9oL ?]tRoo6E 6L/`S7_L^ ^^!^+^5^>L)Lu"^2L)Lu"^&L)Lu"^L)Lu"^L)Lu"^LL/HoL#g%Z7L%L/HoL#g%Z7LL/HoL#g%Z7LL/HoL#g%Z7L%L/HoL#g%Z7LL/HoL#g%Z7L%L/HoL#g%Z7L%L/HoL#g%Z7#VKL#lYL)#mzLLL"rLLLLLLVL91rLLL!b L%b^^ QL^LLLLLLVL91rLLL{L"rLL{L"rL9LLL=VL-LyLVLwLLL!L-L!LVL)Lb LLyfL~LzL!L)QL|CRr9L=L)LL!LVLLL!L-QLtVf LLxL9L%LLwLL}L)LL=VLLLLLLLLva!L-LLv1R7pF7q 7r7sr#"7ts#K7ur#(07vq#f7ws#>37xq#7ys#&]7zs#(07{q#W7|r#f7}s#;W7~LL!7pL?ADL%7pL?[ 7L7pL?.L7pL?S9L7pL?-2LLLLL-LLLLLLLLL)LLLLvLL)LLLvLL?. L?y#L?]l36L?L?@L ? L-?/L?iL? L? >L ?;L)?GL ?ӳ0R 6M 6L/`S7_HL^ ^^^&L)L"^ L)L"^L)L"^9LL%z9L)L)LzrrL/L!LL/1&LLfC)L)$L)LL)f^ )Lf )L^)L-LV@)L%2_)LL#.Z!rR/QL/V(LL^ ^^&^C9LL"^7QL9LL)L3^"QLUL9LL)LL;^LLL"rLL9L=7777#K7#˿7#ſ7#lY7#a17LL!7L?{2#L7L?̨gL7L?t=7LLL)LLvLL?-XL-? ML)?4}rL?R6! 6#L/`S7_L^ ^^^&L)L"^L)L"^_L)L"^9LL!z9LL%z9LLzLLQLLL)L)L*RLLLLV%Lr9L=LLL9LL-L3R9L=!LLLLLLL9LvLLLL)LVLL)LVLLLVk+Lg LLVLL L%j LLVl^6LLf LaLw fL LvL^ ^L/*LL^ ^^J^QLL쭥^QL L f LL LVL L L%L)^iQL L f LL LVL L L%j Ll^$LLf LaL^L/*LL-R^R_l^XLLf LaLuf6LL%LVLLL^ LLL9V^ ^L/*QRrLL-L-L-L9#. 6?)#!|6@#-X6A)#7.6B)#&]6C5#ua6D)#MR6E#ܖ26F#2m'6G#6H#;6I#ſ6J#6K5#}~&6L)#K6M)#>36N #6O #. 6P#W6Q# 6R%#t,96S# 6T#]6U!# M6V## 6W#N6X#w6Y5#a16Z#W6[)#O176\#G6]#!I6^5#lY6_ #16`)#T6a#V6b #۰126c # 6d# 6e)#(06f#׹6g#6h#f6i%#3U=6j# S6kmLpLsLvLwLyLL{6z1Lz?높kHL|LL}LL}LQL~L@LLEL%zLELLL\LNLLiLLL@LLLvL # Maintained at https://github.com/Tronic/cmake-modules # Please send your improvements as pull requests on Github. # Find another package and make it a dependency of the current package. # This also automatically forwards the "REQUIRED" argument. # Usage: libfind_package( [extra args to find_package]) macro (libfind_package PREFIX PKG) set(${PREFIX}_args ${PKG} ${ARGN}) if (${PREFIX}_FIND_REQUIRED) set(${PREFIX}_args ${${PREFIX}_args} REQUIRED) endif() find_package(${${PREFIX}_args}) set(${PREFIX}_DEPENDENCIES ${${PREFIX}_DEPENDENCIES};${PKG}) unset(${PREFIX}_args) endmacro() # A simple wrapper to make pkg-config searches a bit easier. # Works the same as CMake's internal pkg_check_modules but is always quiet. macro (libfind_pkg_check_modules) find_package(PkgConfig QUIET) if (PKG_CONFIG_FOUND) pkg_check_modules(${ARGN} QUIET) endif() endmacro() # Avoid useless copy&pasta by doing what most simple libraries do anyway: # pkg-config, find headers, find library. # Usage: libfind_pkg_detect( FIND_PATH [other args] FIND_LIBRARY [other args]) # E.g. libfind_pkg_detect(SDL2 sdl2 FIND_PATH SDL.h PATH_SUFFIXES SDL2 FIND_LIBRARY SDL2) function (libfind_pkg_detect PREFIX) # Parse arguments set(argname pkgargs) foreach (i ${ARGN}) if ("${i}" STREQUAL "FIND_PATH") set(argname pathargs) elseif ("${i}" STREQUAL "FIND_LIBRARY") set(argname libraryargs) else() set(${argname} ${${argname}} ${i}) endif() endforeach() if (NOT pkgargs) message(FATAL_ERROR "libfind_pkg_detect requires at least a pkg_config package name to be passed.") endif() # Find library libfind_pkg_check_modules(${PREFIX}_PKGCONF ${pkgargs}) if (pathargs) find_path(${PREFIX}_INCLUDE_DIR NAMES ${pathargs} HINTS ${${PREFIX}_PKGCONF_INCLUDE_DIRS}) endif() if (libraryargs) find_library(${PREFIX}_LIBRARY NAMES ${libraryargs} HINTS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}) endif() endfunction() # Extracts a version #define from a version.h file, output stored to _VERSION. # Usage: libfind_version_header(Foobar foobar/version.h FOOBAR_VERSION_STR) # Fourth argument "QUIET" may be used for silently testing different define names. # This function does nothing if the version variable is already defined. function (libfind_version_header PREFIX VERSION_H DEFINE_NAME) # Skip processing if we already have a version or if the include dir was not found if (${PREFIX}_VERSION OR NOT ${PREFIX}_INCLUDE_DIR) return() endif() set(quiet ${${PREFIX}_FIND_QUIETLY}) # Process optional arguments foreach(arg ${ARGN}) if (arg STREQUAL "QUIET") set(quiet TRUE) else() message(AUTHOR_WARNING "Unknown argument ${arg} to libfind_version_header ignored.") endif() endforeach() # Read the header and parse for version number set(filename "${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") if (NOT EXISTS ${filename}) if (NOT quiet) message(AUTHOR_WARNING "Unable to find ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") endif() return() endif() file(READ "${filename}" header) string(REGEX REPLACE ".*#[ \t]*define[ \t]*${DEFINE_NAME}[ \t]*\"([^\n]*)\".*" "\\1" match "${header}") # No regex match? if (match STREQUAL header) if (NOT quiet) message(AUTHOR_WARNING "Unable to find \#define ${DEFINE_NAME} \"\" from ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") endif() return() endif() # Export the version string set(${PREFIX}_VERSION "${match}" PARENT_SCOPE) endfunction() # Do the final processing once the paths have been detected. # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain # all the variables, each of which contain one include directory. # Ditto for ${PREFIX}_PROCESS_LIBS and library files. # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. # Also handles errors in case library detection was required, etc. function (libfind_process PREFIX) # Skip processing if already processed during this configuration run if (${PREFIX}_FOUND) return() endif() set(found TRUE) # Start with the assumption that the package was found # Did we find any files? Did we miss includes? These are for formatting better error messages. set(some_files FALSE) set(missing_headers FALSE) # Shorthands for some variables that we need often set(quiet ${${PREFIX}_FIND_QUIETLY}) set(required ${${PREFIX}_FIND_REQUIRED}) set(exactver ${${PREFIX}_FIND_VERSION_EXACT}) set(findver "${${PREFIX}_FIND_VERSION}") set(version "${${PREFIX}_VERSION}") # Lists of config option names (all, includes, libs) unset(configopts) set(includeopts ${${PREFIX}_PROCESS_INCLUDES}) set(libraryopts ${${PREFIX}_PROCESS_LIBS}) # Process deps to add to foreach (i ${PREFIX} ${${PREFIX}_DEPENDENCIES}) if (DEFINED ${i}_INCLUDE_OPTS OR DEFINED ${i}_LIBRARY_OPTS) # The package seems to export option lists that we can use, woohoo! list(APPEND includeopts ${${i}_INCLUDE_OPTS}) list(APPEND libraryopts ${${i}_LIBRARY_OPTS}) else() # If plural forms don't exist or they equal singular forms if ((NOT DEFINED ${i}_INCLUDE_DIRS AND NOT DEFINED ${i}_LIBRARIES) OR ({i}_INCLUDE_DIR STREQUAL ${i}_INCLUDE_DIRS AND ${i}_LIBRARY STREQUAL ${i}_LIBRARIES)) # Singular forms can be used if (DEFINED ${i}_INCLUDE_DIR) list(APPEND includeopts ${i}_INCLUDE_DIR) endif() if (DEFINED ${i}_LIBRARY) list(APPEND libraryopts ${i}_LIBRARY) endif() else() # Oh no, we don't know the option names message(FATAL_ERROR "We couldn't determine config variable names for ${i} includes and libs. Aieeh!") endif() endif() endforeach() if (includeopts) list(REMOVE_DUPLICATES includeopts) endif() if (libraryopts) list(REMOVE_DUPLICATES libraryopts) endif() string(REGEX REPLACE ".*[ ;]([^ ;]*(_INCLUDE_DIRS|_LIBRARIES))" "\\1" tmp "${includeopts} ${libraryopts}") if (NOT tmp STREQUAL "${includeopts} ${libraryopts}") message(AUTHOR_WARNING "Plural form ${tmp} found in config options of ${PREFIX}. This works as before but is now deprecated. Please only use singular forms INCLUDE_DIR and LIBRARY, and update your find scripts for LibFindMacros > 2.0 automatic dependency system (most often you can simply remove the PROCESS variables entirely).") endif() # Include/library names separated by spaces (notice: not CMake lists) unset(includes) unset(libs) # Process all includes and set found false if any are missing foreach (i ${includeopts}) list(APPEND configopts ${i}) if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND") list(APPEND includes "${${i}}") else() set(found FALSE) set(missing_headers TRUE) endif() endforeach() # Process all libraries and set found false if any are missing foreach (i ${libraryopts}) list(APPEND configopts ${i}) if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND") list(APPEND libs "${${i}}") else() set (found FALSE) endif() endforeach() # Version checks if (found AND findver) if (NOT version) message(WARNING "The find module for ${PREFIX} does not provide version information, so we'll just assume that it is OK. Please fix the module or remove package version requirements to get rid of this warning.") elseif (version VERSION_LESS findver OR (exactver AND NOT version VERSION_EQUAL findver)) set(found FALSE) set(version_unsuitable TRUE) endif() endif() # If all-OK, hide all config options, export variables, print status and exit if (found) foreach (i ${configopts}) mark_as_advanced(${i}) endforeach() if (NOT quiet) message(STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") if (LIBFIND_DEBUG) message(STATUS " ${PREFIX}_DEPENDENCIES=${${PREFIX}_DEPENDENCIES}") message(STATUS " ${PREFIX}_INCLUDE_OPTS=${includeopts}") message(STATUS " ${PREFIX}_INCLUDE_DIRS=${includes}") message(STATUS " ${PREFIX}_LIBRARY_OPTS=${libraryopts}") message(STATUS " ${PREFIX}_LIBRARIES=${libs}") endif() set (${PREFIX}_INCLUDE_OPTS ${includeopts} PARENT_SCOPE) set (${PREFIX}_LIBRARY_OPTS ${libraryopts} PARENT_SCOPE) set (${PREFIX}_INCLUDE_DIRS ${includes} PARENT_SCOPE) set (${PREFIX}_LIBRARIES ${libs} PARENT_SCOPE) set (${PREFIX}_FOUND TRUE PARENT_SCOPE) endif() return() endif() # Format messages for debug info and the type of error set(vars "Relevant CMake configuration variables:\n") foreach (i ${configopts}) mark_as_advanced(CLEAR ${i}) set(val ${${i}}) if ("${val}" STREQUAL "${i}-NOTFOUND") set (val "") elseif (val AND NOT EXISTS ${val}) set (val "${val} (does not exist)") else() set(some_files TRUE) endif() set(vars "${vars} ${i}=${val}\n") endforeach() set(vars "${vars}You may use CMake GUI, cmake -D or ccmake to modify the values. Delete CMakeCache.txt to discard all values and force full re-detection if necessary.\n") if (version_unsuitable) set(msg "${PREFIX} ${${PREFIX}_VERSION} was found but") if (exactver) set(msg "${msg} only version ${findver} is acceptable.") else() set(msg "${msg} version ${findver} is the minimum requirement.") endif() else() if (missing_headers) set(msg "We could not find development headers for ${PREFIX}. Do you have the necessary dev package installed?") elseif (some_files) set(msg "We only found some files of ${PREFIX}, not all of them. Perhaps your installation is incomplete or maybe we just didn't look in the right place?") if(findver) set(msg "${msg} This could also be caused by incompatible version (if it helps, at least ${PREFIX} ${findver} should work).") endif() else() set(msg "We were unable to find package ${PREFIX}.") endif() endif() # Fatal error out if REQUIRED if (required) set(msg "REQUIRED PACKAGE NOT FOUND\n${msg} This package is REQUIRED and you need to install it or adjust CMake configuration in order to continue building ${CMAKE_PROJECT_NAME}.") message(FATAL_ERROR "${msg}\n${vars}") endif() # Otherwise just print a nasty warning if (NOT quiet) message(WARNING "WARNING: MISSING PACKAGE\n${msg} This package is NOT REQUIRED and you may ignore this warning but by doing so you may miss some functionality of ${CMAKE_PROJECT_NAME}. \n${vars}") endif() endfunction() neko-2-4-0/cmake/NekoConfig.cmake.in000066400000000000000000000017141464615675700172100ustar00rootroot00000000000000# - Config file for the Neko package # It defines the following variables # NEKO_INCLUDE_DIRS - include directories for Neko # NEKO_LIBRARIES - libraries to link against # NEKO_EXECUTABLE - the Neko VM executable # NEKOC_EXECUTABLE - the Neko compiler executable # NEKOML_EXECUTABLE - the NekoML compiler executable # NEKOTOOLS_EXECUTABLE - the nekotools executable @PACKAGE_INIT@ # Our library dependencies (contains definitions for IMPORTED targets) if(NOT TARGET neko) include("@PACKAGE_NEKO_TARGETS_FILE@") endif() # Use set instead of set_and_check, which doesn't handle lists properly # https://gitlab.kitware.com/cmake/cmake/issues/16219 set(NEKO_INCLUDE_DIRS "@PACKAGE_NEKO_INCLUDE_DIRS@") # These are IMPORTED targets created by NekoTargets.cmake set(NEKO_LIBRARIES libneko) set(NEKO_EXECUTABLE nekovm) set(NEKOC_EXECUTABLE nekoc) set(NEKOML_EXECUTABLE nekoml) set(NEKOTOOLS_EXECUTABLE nekotools) check_required_components(Neko) neko-2-4-0/cmake/flatten.cmake.in000066400000000000000000000017251464615675700166250ustar00rootroot00000000000000# Detect if the install is run by CPack. if (${CMAKE_INSTALL_PREFIX} MATCHES "/_CPack_Packages/.*/(TGZ|ZIP)/") # Flatten the directory structure such that everything except the header files is placed in root. file(GLOB bin_files LIST_DIRECTORIES FALSE ${CMAKE_INSTALL_PREFIX}/bin/*) file(GLOB lib_files LIST_DIRECTORIES FALSE ${CMAKE_INSTALL_PREFIX}/lib/* ${CMAKE_INSTALL_PREFIX}/lib/**/*) foreach(file ${bin_files} ${lib_files}) get_filename_component(file_name ${file} NAME) execute_process( COMMAND ${CMAKE_COMMAND} -E rename ${file} ${CMAKE_INSTALL_PREFIX}/${file_name} ) endforeach() execute_process( COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_INSTALL_PREFIX}/bin) execute_process( COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_INSTALL_PREFIX}/lib) # install additional files file ( INSTALL @CMAKE_SOURCE_DIR@/README.md @CMAKE_SOURCE_DIR@/LICENSE @CMAKE_SOURCE_DIR@/CHANGES DESTINATION ${CMAKE_INSTALL_PREFIX} ) endif() neko-2-4-0/cmake/ldconfig.cmake000066400000000000000000000004611464615675700163440ustar00rootroot00000000000000# Detect if the install is run by CPack. if (NOT CMAKE_INSTALL_PREFIX MATCHES "/_CPack_Packages/.*/(TGZ|ZIP)/") message(STATUS "Running: ldconfig") execute_process(COMMAND "ldconfig" RESULT_VARIABLE ldconfig_result) if (NOT ldconfig_result EQUAL 0) message(WARNING "ldconfig failed") endif() endif()neko-2-4-0/cmake/package_choco.cmake000066400000000000000000000037741464615675700173370ustar00rootroot00000000000000find_package(Git REQUIRED) # format CHOCO_VERSION if(DEFINED ENV{TAG_RELEASE}) set(CHOCO_VERSION ${NEKO_VERSION}) else() execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD OUTPUT_VARIABLE COMMIT_SHA OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${GIT_EXECUTABLE} show -s --format=%cI HEAD OUTPUT_VARIABLE COMMIT_TIME OUTPUT_STRIP_TRAILING_WHITESPACE ) string(SUBSTRING ${COMMIT_TIME} 0 19 COMMIT_TIME) string(REGEX REPLACE [^0-9] "" COMMIT_TIME ${COMMIT_TIME}) set(CHOCO_VERSION ${NEKO_VERSION}-SNAP${COMMIT_TIME}) endif() message(STATUS "building package version ${CHOCO_VERSION} using ${bin_archive}") get_filename_component(bin_archive_dir ${bin_archive} DIRECTORY) execute_process( COMMAND ${CMAKE_COMMAND} -E tar x ${bin_archive} WORKING_DIRECTORY ${bin_archive_dir} ) configure_file( ${source_dir}/extra/neko.nuspec ${bin_archive_dir}/${bin_archive_name_we}/neko.nuspec @ONLY ) configure_file( ${source_dir}/extra/chocolatey/chocolateyInstall.ps1 ${bin_archive_dir}/${bin_archive_name_we}/chocolateyInstall.ps1 @ONLY ) configure_file( ${source_dir}/extra/chocolatey/chocolateyUninstall.ps1 ${bin_archive_dir}/${bin_archive_name_we}/chocolateyUninstall.ps1 @ONLY ) configure_file( ${source_dir}/LICENSE ${bin_archive_dir}/${bin_archive_name_we}/LICENSE @ONLY ) configure_file( ${source_dir}/extra/chocolatey/VERIFICATION.txt ${bin_archive_dir}/${bin_archive_name_we}/VERIFICATION.txt @ONLY ) execute_process( COMMAND choco pack WORKING_DIRECTORY ${bin_archive_dir}/${bin_archive_name_we} ) file(GLOB nupkg ${bin_archive_dir}/${bin_archive_name_we}/*.nupkg ) get_filename_component(nupkg_name ${nupkg} NAME) message(STATUS "created ${nupkg_name}") file(COPY ${nupkg} DESTINATION ${bin_archive_dir}) file(REMOVE_RECURSE ${bin_archive_dir}/${bin_archive_name_we}) if(DEFINED ENV{APPVEYOR}) message(STATUS "pushing ${nupkg_name} to AppVeyor feeds") execute_process( COMMAND appveyor PushArtifact ${nupkg_name} WORKING_DIRECTORY ${bin_archive_dir} ) endif()neko-2-4-0/cmake/patch_apache.cmake000066400000000000000000000007631464615675700171640ustar00rootroot00000000000000# https://github.com/apache/httpd/commit/dd4561dd17a669a8c1757ada0ca875dfa840d0e7 # https://github.com/apache/httpd/pull/343 set(cmakelists ${apache_source}/CMakeLists.txt) file(READ ${cmakelists} content) string(REPLACE "pcre2-8d.lib" "pcre2-8-staticd.lib" content "${content}" ) string(REPLACE "pcre2-8.lib" "pcre2-8-static.lib" content "${content}" ) string(REPLACE "\"-DHAVE_PCRE2\"" "\"-DHAVE_PCRE2 -DPCRE2_STATIC\"" content "${content}" ) file(WRITE ${cmakelists} "${content}") neko-2-4-0/cmake/patch_apr-util.cmake000066400000000000000000000010021464615675700174630ustar00rootroot00000000000000# This is necessary so that the CMAKE_FIND_LIBRARY_SUFFIXES is preserved when # locating expat, since it gets reset when PROJECT is called set(cmakelists "${apr-util_source}/CMakeLists.txt") file(READ ${cmakelists} content) string(REPLACE "PROJECT(APR-Util C) CMAKE_MINIMUM_REQUIRED(VERSION 2.8) FIND_PACKAGE(OpenSSL) FIND_PACKAGE(EXPAT)" "CMAKE_MINIMUM_REQUIRED(VERSION 2.8) FIND_PACKAGE(OpenSSL) FIND_PACKAGE(EXPAT) PROJECT(APR-Util C)" content "${content}" ) file(WRITE ${cmakelists} "${content}") neko-2-4-0/cmake/patch_mariadb.cmake000066400000000000000000000005741464615675700173420ustar00rootroot00000000000000# https://jira.mariadb.org/browse/CONC-174 set(cmakelists ${mariadb_source}/CMakeLists.txt) file(READ ${cmakelists} content) # do not use replace /MD with /MT string(REPLACE "STRING(REPLACE \"/MD\" \"/MT\" COMPILER_FLAGS \${COMPILER_FLAGS})" "# STRING(REPLACE \"/MD\" \"/MT\" COMPILER_FLAGS \${COMPILER_FLAGS})" content "${content}" ) file(WRITE ${cmakelists} "${content}") neko-2-4-0/cmake/patch_mbedtls.cmake000066400000000000000000000001651464615675700173710ustar00rootroot00000000000000if (WIN32) file(COPY ${source}/libs/ssl/threading_alt.h DESTINATION ${MbedTLS_source}/include/mbedtls/ ) endif() neko-2-4-0/cmake/source_archive_fat.cmake000066400000000000000000000017341464615675700204160ustar00rootroot00000000000000get_filename_component(source_archive_dir ${source_archive} DIRECTORY) execute_process( COMMAND ${CMAKE_COMMAND} -E tar x ${source_archive} WORKING_DIRECTORY ${source_archive_dir} ) file(RENAME ${source_archive_dir}/${source_archive_name_we} ${source_archive_dir}/${source_archive_fat_name_we}) file(GLOB archives LIST_DIRECTORIES FALSE ${bin_dir}/${lib_src_dir}/* ) file(COPY ${archives} DESTINATION ${source_archive_dir}/${source_archive_fat_name_we}/${lib_src_dir}) if (${source_archive_fat_name} MATCHES ^.*.zip$) execute_process( COMMAND ${CMAKE_COMMAND} -E tar cf ${source_archive_fat_name} ${source_archive_dir}/${source_archive_fat_name_we} --format=zip WORKING_DIRECTORY ${source_archive_dir} ) else() execute_process( COMMAND ${CMAKE_COMMAND} -E tar czf ${source_archive_fat_name} ${source_archive_dir}/${source_archive_fat_name_we} WORKING_DIRECTORY ${source_archive_dir} ) endif() file(REMOVE_RECURSE ${source_archive_dir}/${source_archive_fat_name_we}) neko-2-4-0/cmake/uninstall.cmake.in000066400000000000000000000021321464615675700171720ustar00rootroot00000000000000# https://cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file)neko-2-4-0/cmake/upload_to_ppa.cmake000066400000000000000000000061411464615675700174060ustar00rootroot00000000000000find_package(Git REQUIRED) set(neko_debian_dir ${bin_dir}/neko-debian) # format SNAPSHOT_VERSION execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD OUTPUT_VARIABLE COMMIT_SHA OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${GIT_EXECUTABLE} show -s --format=%ct HEAD OUTPUT_VARIABLE COMMIT_TIME OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND date -u -d @${COMMIT_TIME} +%Y%m%d%H%M%S OUTPUT_VARIABLE COMMIT_TIME OUTPUT_STRIP_TRAILING_WHITESPACE ) set(SNAPSHOT_VERSION ${NEKO_VERSION}+1SNAPSHOT${COMMIT_TIME}+${COMMIT_SHA}) message(STATUS "building source package version ${SNAPSHOT_VERSION}") message(STATUS "setting up neko-debian repo") if (EXISTS ${neko_debian_dir}) execute_process( COMMAND ${GIT_EXECUTABLE} fetch --all WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND ${GIT_EXECUTABLE} clean -fx WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND ${GIT_EXECUTABLE} reset --hard HEAD WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND ${GIT_EXECUTABLE} tag -d upstream/${SNAPSHOT_VERSION} WORKING_DIRECTORY ${neko_debian_dir} ) else() execute_process( COMMAND ${GIT_EXECUTABLE} clone https://github.com/HaxeFoundation/neko-debian.git ${neko_debian_dir} ) endif() foreach(branch upstream next) execute_process( COMMAND ${GIT_EXECUTABLE} checkout ${branch} WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND ${GIT_EXECUTABLE} reset --hard origin/${branch} WORKING_DIRECTORY ${neko_debian_dir} ) endforeach() message(STATUS "import changes from source archive to neko-debian") get_filename_component(source_archive_name ${source_archive} NAME) file(COPY ${source_archive} DESTINATION ${bin_dir}) file(RENAME ${bin_dir}/${source_archive_name} ${bin_dir}/neko_${SNAPSHOT_VERSION}.orig.tar.gz) execute_process( COMMAND gbp import-orig ${bin_dir}/neko_${SNAPSHOT_VERSION}.orig.tar.gz -u ${SNAPSHOT_VERSION} --debian-branch=next WORKING_DIRECTORY ${neko_debian_dir} ) set(distros trusty xenial zesty artful bionic ) if (DEFINED ENV{PPA}) set(PPA $ENV{PPA}) else() set(PPA "ppa:haxe/snapshots") endif() foreach(distro ${distros}) message(STATUS "backporting to ${distro} and will upload to ${PPA}") execute_process( COMMAND ${GIT_EXECUTABLE} checkout . WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND ${GIT_EXECUTABLE} checkout next-${distro} WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND ${GIT_EXECUTABLE} clean -fx WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND ${GIT_EXECUTABLE} reset --hard origin/next-${distro} WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND ${GIT_EXECUTABLE} merge next -m "merge" WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND dch -v "${SNAPSHOT_VERSION}-1" --urgency low "snapshot build" WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND debuild -S -sa WORKING_DIRECTORY ${neko_debian_dir} ) execute_process( COMMAND backportpackage -d ${distro} --upload ${PPA} --yes neko_${SNAPSHOT_VERSION}-1.dsc WORKING_DIRECTORY ${bin_dir} ) endforeach()neko-2-4-0/deploy.sh000077500000000000000000000011511464615675700143250ustar00rootroot00000000000000#!/bin/bash set -ex if [ -z $haxeci_decrypt ]; then echo "haxeci_decrypt is unset, skip deploy" exit fi openssl aes-256-cbc -k "$haxeci_decrypt" -in haxeci_ssh.enc -out haxeci_ssh -d chmod 600 haxeci_ssh eval `ssh-agent -s` ssh-add haxeci_ssh openssl aes-256-cbc -k "$haxeci_decrypt" -in haxeci_sec.gpg.enc -out haxeci_sec.gpg -d gpg --allow-secret-key-import --import haxeci_sec.gpg sudo apt-get install devscripts git-buildpackage ubuntu-dev-tools dh-make dh-apache2 -y git config --global user.name "${DEBFULLNAME}" git config --global user.email "${DEBEMAIL}" pushd build make upload_to_ppa popd neko-2-4-0/extra/000077500000000000000000000000001464615675700136175ustar00rootroot00000000000000neko-2-4-0/extra/Brewfile-STATIC_DEPS_ALL000066400000000000000000000001131464615675700175640ustar00rootroot00000000000000brew "cmake" brew "ninja" brew "pkg-config" brew "automake" brew "libtool" neko-2-4-0/extra/Brewfile-STATIC_DEPS_NONE000066400000000000000000000001611464615675700177160ustar00rootroot00000000000000brew "cmake" brew "ninja" brew "pkg-config" brew "bdw-gc" brew "mariadb-connector-c" brew "mbedtls" brew "pcre2" neko-2-4-0/extra/chocolatey/000077500000000000000000000000001464615675700157515ustar00rootroot00000000000000neko-2-4-0/extra/chocolatey/VERIFICATION.txt000066400000000000000000000002641464615675700203760ustar00rootroot00000000000000Packager (Andy Li) is a member of the Haxe Foundation. The files included in this package are the "Windows Binaries" available at https://github.com/HaxeFoundation/neko/releases.neko-2-4-0/extra/chocolatey/chocolateyInstall.ps1.template000066400000000000000000000015521464615675700236740ustar00rootroot00000000000000$packDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" Install-ChocolateyZipPackage ` -PackageName 'neko' ` -UnzipLocation "$packDir" ` -Url "$packDir\neko-*-win.zip" ` -Checksum '::CHECKSUM32::' ` -ChecksumType 'sha256' ` -Url64bit "$packDir\neko-*-win64.zip" ` -Checksum64 '::CHECKSUM64::' ` -ChecksumType64 'sha256' $nekoDir = "$(Get-Item "$packDir/neko-*-win*" -Exclude "*.zip")" # Install the dll files to C:\ProgramData\chocolatey\bin # It is because they are loaded by other neko binaries, e.g. haxelib.exe $chocoBin = Join-Path $env:ChocolateyInstall 'bin' $dllFiles = @('gcmt-dll.dll', 'neko.dll') foreach ($file in $dllFiles) { Copy-Item "$nekoDir/$file" "$chocoBin" } # Set NEKOPATH such that the ndll files can be loaded. Install-ChocolateyEnvironmentVariable -VariableName NEKOPATH -VariableValue $nekoDir neko-2-4-0/extra/chocolatey/chocolateyUninstall.ps1000066400000000000000000000004741464615675700224270ustar00rootroot00000000000000# Remove the dll files from C:\ProgramData\chocolatey\bin $chocoBin = Join-Path $env:ChocolateyInstall 'bin' $dllFiles = @('gcmt-dll.dll', 'neko.dll') foreach ($file in $dllFiles) { $dllFile = Join-Path $chocoBin $file Remove-Item "$dllFile" } Uninstall-ChocolateyEnvironmentVariable -VariableName NEKOPATH neko-2-4-0/extra/chocolatey/generatePackage.ps1000066400000000000000000000027721464615675700214540ustar00rootroot00000000000000param ([string] $version) $ErrorActionPreference = "Stop" $SOURCE = ".\extra\chocolatey" $OUTPUT = "$SOURCE\out" If ( $version -eq "" ) { Write-Error "No version parameter was passed in." Exit 1 } # Create empty If ( Test-Path -Path $OUTPUT) { Get-ChildItem -Path $OUTPUT -File | foreach { $_.Delete()} } Else { New-Item -Path $OUTPUT -ItemType "directory" -Force > $null } Function Copy-File { param ([string] $file) Write-Host "Copying $file" Copy-Item $file $OUTPUT } # Copy over zipped up binaries $file32 = "neko-$version-win.zip" $file64 = "neko-$version-win64.zip" ForEach ($file in @(".\windows-vs2017-binaries\$file32", ".\windows64-vs2017-binaries\$file64")) { If ( ! (Test-Path -Path $file -PathType Leaf) ) { Write-Error "File $file missing" Exit 2 } Copy-File $file } # Generate install script Write-Host "Generating install script" # Load template $template = (Get-Content -Path "$source\chocolateyInstall.ps1.template" -Raw) # Get checksums $checksum32 = (Get-FileHash $OUTPUT\$file32).Hash.ToLower() $checksum64 = (Get-FileHash $OUTPUT\$file64).Hash.ToLower() # Generate install script with correct checksums $installScript = $template -Replace '::CHECKSUM32::',$checksum32 -Replace '::CHECKSUM64::',$checksum64 Out-File -FilePath "$OUTPUT\chocolateyInstall.ps1" -InputObject $installScript -NoNewline # Copy over general files $toCopy = @(".\LICENSE", "$SOURCE\VERIFICATION.txt", "$SOURCE\neko.nuspec","$SOURCE\chocolateyUninstall.ps1") ForEach ($item in $toCopy) { Copy-File $item } neko-2-4-0/extra/chocolatey/neko.nuspec000066400000000000000000000031731464615675700201300ustar00rootroot00000000000000 neko 0.0.0 Neko Haxe Foundation Haxe Foundation https://github.com/HaxeFoundation/neko/blob/master/LICENSE https://nekovm.org/ https://nekovm.org/doc/begin/ https://groups.google.com/forum/#!forum/haxelang https://github.com/HaxeFoundation/neko/issues https://github.com/HaxeFoundation/neko https://github.com/HaxeFoundation/neko/tree/master/extra/chocolatey https://cdn.rawgit.com/andyli/cc07575f598351e0ad74/raw/6a9cae9a136670c0052356b773f8e13bf37c13dd/logo.png false Neko is a lightweight and yet well optimized virtual machine. Neko is a lightweight and yet well optimized virtual machine. The VM can be easily embedded into any application and your libraries can be accessed using the C foreign function interface. https://github.com/HaxeFoundation/neko/blob/master/CHANGES neko haxe vm admin neko-2-4-0/libs/000077500000000000000000000000001464615675700134255ustar00rootroot00000000000000neko-2-4-0/libs/CMakeLists.txt000066400000000000000000000213511464615675700161670ustar00rootroot00000000000000add_subdirectory(common) add_subdirectory(std) if (STATIC_ZLIB) set(ZLIB_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/libs/src/install-prefix -Wno-dev ) if (UNIX) list(APPEND ZLIB_CMAKE_ARGS -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} ${ARG_PIC} ) endif() if (WIN32) set(ZLIB_LIBRARIES optimized ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/zlibstatic.lib debug ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/zlibstaticd.lib ) else() set(ZLIB_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/libz.a) endif() # Get the current config. Borrowed from # https://github.com/Kitware/CMake/blob/bc7d64f896d6e180970cb404cc7699732db34adc/Modules/ExternalProject.cmake if (CMAKE_CFG_INTDIR AND NOT CMAKE_CFG_INTDIR STREQUAL "." AND NOT CMAKE_CFG_INTDIR MATCHES "\\$") set(config ${CMAKE_CFG_INTDIR}) else() set(config $) endif() ExternalProject_Add(Zlib ${EP_CONFIGS} URL https://zlib.net/fossils/zlib-1.2.13.tar.gz https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz URL_HASH SHA256=b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30 CMAKE_ARGS ${ZLIB_CMAKE_ARGS} INSTALL_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/Zlib-build && ${CMAKE_COMMAND} --build . --target install --config ${config} BYPRODUCTS ${ZLIB_LIBRARIES} ) set_target_properties(Zlib PROPERTIES ${EP_PROPS}) set(ZLIB_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/libs/src/install-prefix/include) # Download project for fat source archive add_dependencies(download_deps Zlib-download) else() find_package(ZLIB REQUIRED) endif() add_subdirectory(zlib) if (WITH_MYSQL) add_subdirectory(mysql) endif() if (WITH_REGEXP) add_subdirectory(regexp) endif() if (WITH_SQLITE) add_subdirectory(sqlite) endif() if (WITH_SSL) add_subdirectory(ssl) endif() if (WITH_UI) add_subdirectory(ui) endif() if (WITH_APACHE) # Locate Apache if (STATIC_APACHE) if (STATIC_OPENSSL) set(OPENSSL_CONF --with-openssl=${CMAKE_BINARY_DIR}/libs/src/install-prefix) set(OPENSSL_DEP OpenSSL) elseif() set(OPENSSL_CONF "") set(OPENSSL_DEP "") endif() if (STATIC_APR) set(APR_CONF --with-apr=${CMAKE_BINARY_DIR}/libs/src/install-prefix) set(APR_DEP APR) elseif() set(APR_CONF "") set(APR_DEP "") endif() if (STATIC_APRUTIL) set(APRUTIL_CONF --with-apr-util=${CMAKE_BINARY_DIR}/libs/src/install-prefix) set(APRUTIL_DEP APRutil) elseif() set(APRUTIL_CONF "") set(APRUTIL_DEP "") endif() if (STATIC_PCRE2) set(PCRE_CONF --with-pcre=${CMAKE_BINARY_DIR}/libs/src/install-prefix/bin/pcre2-config) set(PCRE_DEP pcre2) elseif() set(PCRE_CONF "") set(PCRE_DEP "") endif() if (STATIC_ZLIB) set(ZLIB_CONF --with-z=${CMAKE_BINARY_DIR}/libs/src/install-prefix) set(ZLIB_DEP Zlib) elseif() set(ZLIB_CONF "") set(ZLIB_DEP "") endif() if (WIN32) set(EXPAT_CONF -DCMAKE_POLICY_DEFAULT_CMP0074=NEW -DEXPAT_ROOT=${CMAKE_BINARY_DIR}/libs/src/install-prefix ) if (${CMAKE_VERSION} VERSION_LESS 3.12) message(WARNING "CMake 3.12 or above is required for building APRutil on Windows") endif() if (${CMAKE_VERSION} VERSION_LESS 3.27) # we need to make sure cmake finds the correct library build # see: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/8225 # this monstrosity is necessary because semicolons have to be escaped for each time the string variable is used string(REPLACE ";" "\\\\\\\\\\\\\;" EXPAT_LIBRARY_SUFFIXES_ESCAPED "${CMAKE_FIND_LIBRARY_SUFFIXES};dMD.lib;MD.lib;dMT.lib;MT.lib;d.lib") list(APPEND EXPAT_CONF "-DCMAKE_FIND_LIBRARY_SUFFIXES=${EXPAT_LIBRARY_SUFFIXES_ESCAPED}") endif() set(EXPAT_DEP EXPAT) elseif() set(EXPAT_CONF "") set(EXPAT_DEP "") endif() if (APPLE) set(APACHE_CFLAGS "-w -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") else() set(APACHE_CFLAGS "-w") endif() if(WIN32) set(APR_CONFIGS CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/libs/src/install-prefix -Wno-dev -DAPR_INSTALL_PRIVATE_H=ON -DINSTALL_PDB=OFF ) else() set(APR_CONFIGS CONFIGURE_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/APR && ./configure --prefix=${CMAKE_BINARY_DIR}/libs/src/install-prefix --enable-shared=no --enable-static=yes --silent BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/APR && make "CFLAGS=${APACHE_CFLAGS}" INSTALL_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/APR && make install ) endif() ExternalProject_Add(APR ${EP_CONFIGS} URL https://archive.apache.org/dist/apr/apr-1.7.2.tar.gz URL_HASH SHA256=3d8999b216f7b6235343a4e3d456ce9379aa9a380ffb308512f133f0c5eb2db9 ${APR_CONFIGS} ) set_target_properties(APR PROPERTIES ${EP_PROPS}) if(WIN32) ExternalProject_Add(EXPAT ${EP_CONFIGS} URL https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-2.5.0.tar.gz URL_HASH SHA256=6b902ab103843592be5e99504f846ec109c1abb692e85347587f237a4ffa1033 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/libs/src/install-prefix -Wno-dev -DEXPAT_SHARED_LIBS=OFF ) set_target_properties(EXPAT PROPERTIES ${EP_PROPS}) set(APRutil_CONFIGS CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/libs/src/install-prefix -Wno-dev -DINSTALL_PDB=OFF ${EXPAT_CONF} PATCH_COMMAND ${CMAKE_COMMAND} -Dapr-util_source=${CMAKE_BINARY_DIR}/libs/src/APRutil -P ${CMAKE_SOURCE_DIR}/cmake/patch_apr-util.cmake ) else() set(APRutil_CONFIGS CONFIGURE_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/APRutil && ./configure --prefix=${CMAKE_BINARY_DIR}/libs/src/install-prefix --silent ${APR_CONF} BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/APRutil && make "CFLAGS=${APACHE_CFLAGS}" INSTALL_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/APRutil && make install ) endif() ExternalProject_Add(APRutil ${EP_CONFIGS} DEPENDS ${APR_DEP} ${EXPAT_DEP} URL https://archive.apache.org/dist/apr/apr-util-1.6.3.tar.gz URL_HASH SHA256=2b74d8932703826862ca305b094eef2983c27b39d5c9414442e9976a9acf1983 ${APRutil_CONFIGS} ) set_target_properties(APRutil PROPERTIES ${EP_PROPS}) if(WIN32) set(Apache_CONFIGS CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/libs/src/install-prefix -Wno-dev -DOPENSSL_ROOT_DIR=${CMAKE_BINARY_DIR}/libs/src/install-prefix -DEXTRA_LIBS=Ws2_32 PATCH_COMMAND PATCH_COMMAND ${CMAKE_COMMAND} -Dapache_source=${CMAKE_BINARY_DIR}/libs/src/Apache -P ${CMAKE_SOURCE_DIR}/cmake/patch_apache.cmake BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/Apache-build && ${CMAKE_COMMAND} --build . --target libhttpd --config ${CMAKE_CFG_INTDIR} INSTALL_COMMAND echo skip install ) set(APACHE_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/libs/src/install-prefix/include ${CMAKE_BINARY_DIR}/libs/src/Apache/include ${CMAKE_BINARY_DIR}/libs/src/Apache/os/win32 ${CMAKE_BINARY_DIR}/libs/src/Apache-build ) set(APACHE_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/libapr-1.lib ${CMAKE_BINARY_DIR}/libs/src/Apache-build/${CMAKE_CFG_INTDIR}/libhttpd.lib ) else() set(Apache_CONFIGS CONFIGURE_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/Apache && ./configure --prefix=${CMAKE_BINARY_DIR}/libs/src/Apache-build --silent ${APR_CONF} ${OPENSSL_CONF} ${PCRE_CONF} ${ZLIB_CONF} ${APRUTIL_CONF} BUILD_COMMAND echo skip build INSTALL_COMMAND echo skip install ) set(APACHE_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/libs/src/install-prefix/include/apr-1 ${CMAKE_BINARY_DIR}/libs/src/Apache/include ${CMAKE_BINARY_DIR}/libs/src/Apache/os/unix ) set(APACHE_LIBRARIES ) endif() ExternalProject_Add(Apache ${EP_CONFIGS} DEPENDS ${APR_DEP} ${APRUTIL_DEP} ${OPENSSL_DEP} ${PCRE_DEP} ${ZLIB_DEP} URL https://archive.apache.org/dist/httpd/httpd-2.4.55.tar.gz https://github.com/HaxeFoundation/neko/files/10745746/httpd-2.4.55.tar.gz URL_HASH SHA256=5276ea8bc6fff31eed5c82132ae51a0b2ee05f9e6b61a00fa877f6cadab3b638 ${Apache_CONFIGS} ) set_target_properties(Apache PROPERTIES ${EP_PROPS}) # Download sources for fat source archive if (WIN32) add_dependencies(download_deps EXPAT-download) endif() add_dependencies(download_deps Apache-download) add_dependencies(download_deps APR-download) add_dependencies(download_deps APRutil-download) else() find_package(APACHE REQUIRED) find_package(APR REQUIRED) set(APACHE_LIBRARIES ${APR_LIBRARIES} ${APRUTIL_LIBRARIES}) if(HTTPD_LIBRARIES) list(APPEND APACHE_LIBRARIES ${HTTPD_LIBRARIES}) endif() set(APACHE_INCLUDE_DIRS ${APACHE_INCLUDE_DIR} ${APR_INCLUDE_DIR} ${APRUTIL_INCLUDE_DIR}) endif() add_subdirectory(mod_neko) add_subdirectory(mod_tora) endif() neko-2-4-0/libs/common/000077500000000000000000000000001464615675700147155ustar00rootroot00000000000000neko-2-4-0/libs/common/CMakeLists.txt000066400000000000000000000002411464615675700174520ustar00rootroot00000000000000configure_file ( "${CMAKE_CURRENT_SOURCE_DIR}/osdef.h.in" "${CMAKE_BINARY_DIR}/osdef.h" ) add_library(socket STATIC socket.c) add_library(sha1 STATIC sha1.c) neko-2-4-0/libs/common/osdef.h.in000066400000000000000000000040061464615675700165730ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef OS_H #define OS_H #if defined(_WIN32) # define OS_WINDOWS #endif #if defined(__APPLE__) || defined(macintosh) # define OS_MAC #endif #if defined(linux) || defined(__linux__) # define OS_LINUX #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) # define OS_BSD #endif #if defined(__GNU__) # define OS_HURD #endif #if defined(__CYGWIN__) # define OS_CYGWIN #endif #if defined(OS_LINUX) || defined(OS_MAC) || defined(OS_BSD) || defined(OS_GNUKBSD) || defined (OS_HURD) || defined (OS_CYGWIN) # define OS_POSIX #endif #if defined(OS_MAC) || defined(OS_BSD) # include #elif !defined(OS_WINDOWS) # include #endif #cmakedefine NEKO_BIG_ENDIAN #ifndef NEKO_BIG_ENDIAN # define NEKO_LITTLE_ENDIAN #endif #ifndef true # define true 1 # define false 0 typedef int bool; #endif #endif /* ************************************************************************ */ neko-2-4-0/libs/common/sha1.c000066400000000000000000000125041464615675700157170ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "osdef.h" #include "sha1.h" #include #include // original code by Steve Reid #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #ifdef NEKO_BIG_ENDIAN # define blk0(i) block[i] #else # define blk0(i) (block[i] = (rol(block[i],24)&0xFF00FF00) \ |(rol(block[i],8)&0x00FF00FF)) #endif #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ ^block[(i+2)&15]^block[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); static void sha1_transform( unsigned int state[5], unsigned char buffer[64] ) { unsigned int a, b, c, d, e; unsigned int block[16]; memcpy(block, buffer, 64); /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } void sha1_init( SHA1_CTX *context ) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } void sha1_update( SHA1_CTX *context, const unsigned char *data, unsigned int len ) { unsigned int i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); sha1_transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64 ) sha1_transform(context->state, (unsigned char *)&data[i]); j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } void sha1_final( SHA1_CTX *context, unsigned char digest[SHA1_SIZE] ) { unsigned int i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } sha1_update(context, (unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) { sha1_update(context, (unsigned char *)"\0", 1); } sha1_update(context, finalcount, 8); for (i = 0; i < SHA1_SIZE; i++) { digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } sha1_transform(context->state, context->buffer); } neko-2-4-0/libs/common/sha1.h000066400000000000000000000030511464615675700157210ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef SHA1_H #define SHA1_H #define SHA1_SIZE 20 typedef unsigned char SHA1_DIGEST[SHA1_SIZE]; typedef struct { unsigned int state[5]; unsigned int count[2]; unsigned char buffer[64]; } SHA1_CTX; void sha1_init( SHA1_CTX *c ); void sha1_update( SHA1_CTX *c, const unsigned char *data, unsigned int len ); void sha1_final( SHA1_CTX *c, SHA1_DIGEST digest ); #endif /* ************************************************************************ */ neko-2-4-0/libs/common/socket.c000066400000000000000000000121141464615675700163500ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "socket.h" #include #ifdef OS_WINDOWS static int init_done = 0; static WSADATA init_data; # define POSIX_LABEL(x) # define HANDLE_EINTR(x) #else # include # include # include # include # include # include # include # include # include # include # include # include # define closesocket close # define SOCKET_ERROR (-1) # define POSIX_LABEL(x) x: # define HANDLE_EINTR(x) if( errno == EINTR ) goto x #endif #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 #endif static int block_error() { #ifdef OS_WINDOWS int err = WSAGetLastError(); if( err == WSAEWOULDBLOCK || err == WSAEALREADY ) #else if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EALREADY ) #endif return PS_BLOCK; return PS_ERROR; } void psock_init() { #ifdef OS_WINDOWS if( !init_done ) { WSAStartup(MAKEWORD(2,0),&init_data); init_done = 1; } #endif } PSOCK psock_create() { PSOCK s = socket(AF_INET,SOCK_STREAM,0); # if defined(OS_MAC) || defined(OS_BSD) if( s != INVALID_SOCKET ) setsockopt(s,SOL_SOCKET,SO_NOSIGPIPE,NULL,0); # endif # ifdef OS_POSIX // we don't want sockets to be inherited in case of exec { int old = fcntl(s,F_GETFD,0); if( old >= 0 ) fcntl(s,F_SETFD,old|FD_CLOEXEC); } # endif return s; } void psock_close( PSOCK s ) { POSIX_LABEL(close_again); if( closesocket(s) ) { HANDLE_EINTR(close_again); } } int psock_send( PSOCK s, const char *buf, int size ) { int ret; POSIX_LABEL(send_again); ret = send(s,buf,size,MSG_NOSIGNAL); if( ret == SOCKET_ERROR ) { HANDLE_EINTR(send_again); return block_error(); } return ret; } int psock_recv( PSOCK s, char *buf, int size ) { int ret; POSIX_LABEL(recv_again); ret = recv(s,buf,size,MSG_NOSIGNAL); if( ret == SOCKET_ERROR ) { HANDLE_EINTR(recv_again); return block_error(); } return ret; } PHOST phost_resolve( const char *host ) { PHOST ip = inet_addr(host); if( ip == INADDR_NONE ) { struct hostent *h; # if defined(OS_WINDOWS) || defined(OS_MAC) || defined(OS_CYGWIN) h = gethostbyname(host); # else struct hostent hbase; char buf[1024]; int errcode; gethostbyname_r(host,&hbase,buf,1024,&h,&errcode); # endif if( h == NULL ) return UNRESOLVED_HOST; ip = *((unsigned int*)h->h_addr); } return ip; } SERR psock_connect( PSOCK s, PHOST host, int port ) { struct sockaddr_in addr; memset(&addr,0,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); *(int*)&addr.sin_addr.s_addr = host; if( connect(s,(struct sockaddr*)&addr,sizeof(addr)) != 0 ) return block_error(); return PS_OK; } SERR psock_set_timeout( PSOCK s, double t ) { #ifdef OS_WINDOWS int time = (int)(t * 1000); #else struct timeval time; time.tv_usec = (int)((t - (int)t)*1000000); time.tv_sec = (int)t; #endif if( setsockopt(s,SOL_SOCKET,SO_SNDTIMEO,(char*)&time,sizeof(time)) != 0 ) return PS_ERROR; if( setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char*)&time,sizeof(time)) != 0 ) return PS_ERROR; return PS_OK; } SERR psock_set_blocking( PSOCK s, int block ) { #ifdef OS_WINDOWS { unsigned long arg = !block; if( ioctlsocket(s,FIONBIO,&arg) != 0 ) return PS_ERROR; } #else { int rights = fcntl(s,F_GETFL); if( rights == -1 ) return PS_ERROR; if( block ) rights &= ~O_NONBLOCK; else rights |= O_NONBLOCK; if( fcntl(s,F_SETFL,rights) == -1 ) return PS_ERROR; } #endif return PS_OK; } SERR psock_set_fastsend( PSOCK s, int fast ) { if( setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char*)&fast,sizeof(fast)) ) return block_error(); return PS_OK; } void psock_wait( PSOCK s ) { # ifdef OS_WINDOWS fd_set set; FD_ZERO(&set); FD_SET(s,&set); select((int)s+1,&set,NULL,NULL,NULL); # else struct pollfd fds; POSIX_LABEL(poll_again); fds.fd = s; fds.events = POLLIN; fds.revents = 0; if( poll(&fds,1,-1) < 0 ) { HANDLE_EINTR(poll_again); } # endif } /* ************************************************************************ */ neko-2-4-0/libs/common/socket.h000066400000000000000000000036241464615675700163630ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef SOCKET_H #define SOCKET_H #include "osdef.h" #ifdef OS_WINDOWS # include typedef SOCKET PSOCK; #else typedef int PSOCK; # define INVALID_SOCKET (-1) #endif typedef unsigned int PHOST; #define UNRESOLVED_HOST ((PHOST)-1) typedef enum { PS_OK = 0, PS_ERROR = -1, PS_BLOCK = -2, } SERR; void psock_init(); PSOCK psock_create(); void psock_close( PSOCK s ); SERR psock_connect( PSOCK s, PHOST h, int port ); SERR psock_set_timeout( PSOCK s, double timeout ); SERR psock_set_blocking( PSOCK s, int block ); SERR psock_set_fastsend( PSOCK s, int fast ); int psock_send( PSOCK s, const char *buf, int size ); int psock_recv( PSOCK s, char *buf, int size ); PHOST phost_resolve( const char *hostname ); #endif /* ************************************************************************ */ neko-2-4-0/libs/mod_neko/000077500000000000000000000000001464615675700152205ustar00rootroot00000000000000neko-2-4-0/libs/mod_neko/CMakeLists.txt000066400000000000000000000013151464615675700177600ustar00rootroot00000000000000 ###################### # mod_neko2.ndll add_library(mod_neko2.ndll MODULE ../../vm/stats.c # FIXME mod_neko.c cgi.c ) target_include_directories(mod_neko2.ndll PRIVATE ${APACHE_INCLUDE_DIRS} ) target_link_libraries(mod_neko2.ndll libneko ${APACHE_LIBRARIES}) # In static Apache case build dependencies first if (STATIC_APACHE) add_dependencies(mod_neko2.ndll Apache) endif() set_target_properties(mod_neko2.ndll PROPERTIES PREFIX "" OUTPUT_NAME mod_neko2 SUFFIX .ndll ) if(APPLE) set_target_properties(mod_neko2.ndll PROPERTIES LINK_FLAGS "-undefined dynamic_lookup ${LINK_FLAGS}" ) endif() install ( TARGETS mod_neko2.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/mod_neko/cgi.c000066400000000000000000000361261464615675700161360ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include "mod_neko.h" DEFINE_KIND(k_mod_neko); #ifndef NEKO_WINDOWS # define strcmpi strcasecmp #endif // recent versions of APR include "stdbool.h" somewhere which causes conflicts. #ifdef bool # undef bool #endif #ifdef APACHE_2_X # define ap_table_get apr_table_get # define ap_table_set apr_table_set # define ap_table_add apr_table_add # define ap_table_do apr_table_do # define REDIRECT HTTP_MOVED_TEMPORARILY #endif #define PARSE_HEADER(start,cursor) \ cursor = start; \ if( *cursor == '"' ) { \ start++; \ cursor++; \ while( *cursor != '"' && *cursor != 0 ) \ cursor++; \ } else { \ while( *cursor != 0 && *cursor != '\r' && *cursor != '\n' && *cursor != '\t' ) \ cursor++; \ } #define HEADERS_NOT_SENT(msg) \ if( c->headers_sent ) { \ buffer b = alloc_buffer("Cannot set "); \ buffer_append(b,msg); \ buffer_append(b," : Headers already sent"); \ bfailure(b); \ } /**

Mod_neko

Apache access when running inside mod_neko.

**/ /** get_cookies : void -> #list Return a cookie list as a (name,value) chained list **/ static value get_cookies() { const char *k = ap_table_get(CONTEXT()->r->headers_in,"Cookie"); char *start, *end; value p = val_null, tmp; if( k == NULL ) return p; while( (start = strchr(k,'=')) != NULL ) { start++; end = start; while( *end != 0 && *end != '\r' && *end != '\n' && *end != ';' ) end++; tmp = alloc_array(3); val_array_ptr(tmp)[0] = copy_string(k,(int)(start-k-1)); val_array_ptr(tmp)[1] = copy_string(start,(int)(end-start)); val_array_ptr(tmp)[2] = p; p = tmp; if( *end != ';' || end[1] != ' ' ) break; k = end + 2; } return p; } /** set_cookie : name:string -> val:string -> void Set a cookie **/ static value set_cookie( value name, value v ) { mcontext *c = CONTEXT(); buffer b; value str; val_check(name,string); val_check(v,string); HEADERS_NOT_SENT("Cookie"); b = alloc_buffer(NULL); val_buffer(b,name); buffer_append(b,"="); val_buffer(b,v); buffer_append(b,";"); str = buffer_to_string(b); ap_table_add(c->r->headers_out,"Set-Cookie",val_string(str)); return val_true; } /** get_host_name : void -> string Get the local host IP **/ static value get_host_name() { mcontext *c = CONTEXT(); return alloc_string( c->r->hostname ); } /** get_client_ip : void -> string Get the connected client IP **/ static value get_client_ip() { #if AP_SERVER_MAJORVERSION_NUMBER >= 2 && AP_SERVER_MINORVERSION_NUMBER >= 4 return alloc_string( CONTEXT()->r->useragent_ip ); #else return alloc_string( CONTEXT()->r->connection->remote_ip ); #endif } /** get_uri : void -> string Get the original URI requested by the client (before any internal redirection) **/ static value get_uri() { request_rec *r = CONTEXT()->r; while( r->prev != NULL ) r = r->prev; return alloc_string( r->uri ); } /** redirect : string -> void Redirect the client to another page (Location header) **/ static value redirect( value s ) { mcontext *c = CONTEXT(); val_check(s,string); HEADERS_NOT_SENT("Redirection"); ap_table_set(c->r->headers_out,"Location",val_string(s)); c->r->status = REDIRECT; return val_true; } /** set_return_code : int -> void Set the HTTP return code **/ static value set_return_code( value i ) { mcontext *c = CONTEXT(); val_check(i,int); HEADERS_NOT_SENT("Return code"); c->r->status = val_int(i); return val_true; } /** set_header : name:string -> val:string -> void Set a HTTP header value **/ static value set_header( value s, value k ) { mcontext *c = CONTEXT(); val_check(s,string); val_check(k,string); HEADERS_NOT_SENT("Header"); if( strcmpi(val_string(s),"Content-Type") == 0 ) { c->content_type = alloc_string(val_string(k)); c->r->content_type = val_string(c->content_type); } else ap_table_set(c->r->headers_out,val_string(s),val_string(k)); return val_true; } /** get_client_header : name:string -> string? Get a HTTP header sent by the client **/ static value get_client_header( value s ) { mcontext *c = CONTEXT(); val_check(s,string); return alloc_string( ap_table_get(c->r->headers_in,val_string(s)) ); } static int store_table( void *r, const char *key, const char *val ) { value a; if( key == NULL || val == NULL ) return 1; a = alloc_array(3); val_array_ptr(a)[0] = alloc_string(key); val_array_ptr(a)[1] = alloc_string(val); val_array_ptr(a)[2] = *(value*)r; *((value*)r) = a; return 1; } /** get_client_headers : void -> string list Get all the HTTP client headers **/ static value get_client_headers() { value r = val_null; ap_table_do(store_table,&r,CONTEXT()->r->headers_in,NULL); return r; } /** get_params_string : void -> string Return the whole parameters string **/ static value get_params_string() { return alloc_string(CONTEXT()->r->args); } /** get_post_data : void -> string Return the whole unparsed POST string **/ static value get_post_data() { return CONTEXT()->post_data; } static char *memfind( char *mem, int mlen, const char *v ) { char *found; int len = (int)strlen(v); if( len == 0 ) return mem; while( (found = memchr(mem,*v,mlen)) != NULL ) { if( (int)(found - mem) + len > mlen ) break; if( memcmp(found,v,len) == 0 ) return found; mlen -= (int)(found - mem + 1); mem = found + 1; } return NULL; } #define BUFSIZE 1024 static void fill_buffer( mcontext *c, value buf, int *len ) { int pos = *len; while( pos < BUFSIZE ) { int k = ap_get_client_block(c->r,val_string(buf)+pos,BUFSIZE-pos); if( k == 0 ) break; pos += k; } *len = pos; } static value discard_body( mcontext *c ) { char buf[256]; while( ap_get_client_block(c->r,buf,256) > 0 ) { } neko_error(); } /** parse_multipart_data : onpart:function:2 -> ondata:function:3 -> void Incrementally parse the multipart data. call [onpart(name,filename)] for each part found and [ondata(buf,pos,len)] when some data is available **/ static value parse_multipart_data( value onpart, value ondata ) { value buf; int len = 0; mcontext *c = CONTEXT(); const char *ctype = ap_table_get(c->r->headers_in,"Content-Type"); value boundstr; val_check_function(onpart,2); val_check_function(ondata,3); buf = alloc_empty_string(BUFSIZE); if( !ctype || strstr(ctype,"multipart/form-data") == NULL ) return val_null; // extract boundary value { const char *boundary, *bend; if( (boundary = strstr(ctype,"boundary=")) == NULL ) neko_error(); boundary += 9; PARSE_HEADER(boundary,bend); len = (int)(bend - boundary); boundstr = alloc_empty_string(len+2); if( val_strlen(boundstr) > BUFSIZE / 2 ) neko_error(); val_string(boundstr)[0] = '-'; val_string(boundstr)[1] = '-'; memcpy(val_string(boundstr)+2,boundary,len); } len = 0; if( !ap_should_client_block(c->r) ) neko_error(); while( true ) { char *name, *end_name, *filename, *end_file_name, *data; int pos; // refill buffer // we assume here that the the whole multipart header can fit in the buffer fill_buffer(c,buf,&len); // is boundary at the beginning of buffer ? if( len < val_strlen(boundstr) || memcmp(val_string(buf),val_string(boundstr),val_strlen(boundstr)) != 0 ) return discard_body(c); name = memfind(val_string(buf),len,"Content-Disposition:"); if( name == NULL ) break; name = memfind(name,len - (int)(name - val_string(buf)),"name="); if( name == NULL ) return discard_body(c); name += 5; PARSE_HEADER(name,end_name); data = memfind(end_name,len - (int)(end_name - val_string(buf)),"\r\n\r\n"); if( data == NULL ) return discard_body(c); filename = memfind(name,(int)(data - name),"filename="); if( filename != NULL ) { filename += 9; PARSE_HEADER(filename,end_file_name); } data += 4; pos = (int)(data - val_string(buf)); // send part name val_call2(onpart,copy_string(name,(int)(end_name - name)),filename?copy_string(filename,(int)(end_file_name - filename)):val_null); // read data while( true ) { const char *boundary; // recall buffer memmove(val_string(buf),val_string(buf)+pos,len - pos); len -= pos; pos = 0; fill_buffer(c,buf,&len); // lookup bounds boundary = memfind(val_string(buf),len,val_string(boundstr)); if( boundary == NULL ) { if( len == 0 ) return discard_body(c); // send as much buffer as possible to client if( len < BUFSIZE ) pos = len; else pos = len - val_strlen(boundstr) + 1; val_call3(ondata,buf,alloc_int(0),alloc_int(pos)); } else { // send remaining data pos = (int)(boundary - val_string(buf)); val_call3(ondata,buf,alloc_int(0),alloc_int(pos-2)); // recall memmove(val_string(buf),val_string(buf)+pos,len - pos); len -= pos; break; } } } return val_null; } static value url_decode( const char *in, int len ) { int pin = 0; int pout = 0; value v = alloc_empty_string(len); char *out = (char*)val_string(v); while( len-- > 0 ) { char c = in[pin++]; if( c == '+' ) c = ' '; else if( c == '%' ) { int p1, p2; if( len < 2 ) break; p1 = in[pin++]; p2 = in[pin++]; len -= 2; if( p1 >= '0' && p1 <= '9' ) p1 -= '0'; else if( p1 >= 'a' && p1 <= 'f' ) p1 -= 'a' - 10; else if( p1 >= 'A' && p1 <= 'F' ) p1 -= 'A' - 10; else continue; if( p2 >= '0' && p2 <= '9' ) p2 -= '0'; else if( p2 >= 'a' && p2 <= 'f' ) p2 -= 'a' - 10; else if( p2 >= 'A' && p2 <= 'F' ) p2 -= 'A' - 10; else continue; c = (char)((unsigned char)((p1 << 4) + p2)); } out[pout++] = c; } out[pout] = 0; val_set_size(v,pout); return v; } static void parse_get( value *p, const char *args ) { char *aand, *aeq, *asep; value tmp; while( true ) { aand = strchr(args,'&'); if( aand == NULL ) { asep = strchr(args,';'); aand = asep; } else { asep = strchr(args,';'); if( asep != NULL && asep < aand ) aand = asep; } if( aand != NULL ) *aand = 0; aeq = strchr(args,'='); if( aeq != NULL ) { *aeq = 0; tmp = alloc_array(3); val_array_ptr(tmp)[0] = url_decode(args,(int)(aeq-args)); val_array_ptr(tmp)[1] = url_decode(aeq+1,(int)strlen(aeq+1)); val_array_ptr(tmp)[2] = *p; *p = tmp; *aeq = '='; } if( aand == NULL ) break; *aand = (aand == asep)?';':'&'; args = aand+1; } } /** get_params : void -> #list parse all GET and POST params and return them into a chained list **/ static value get_params() { mcontext *c = CONTEXT(); const char *args = c->r->args; value p = val_null; // PARSE "GET" PARAMS if( args != NULL ) parse_get(&p,args); // PARSE "POST" PARAMS if( c->post_data != NULL ) { const char *ctype = ap_table_get(c->r->headers_in,"Content-Type"); if( ctype == NULL || strstr(ctype,"urlencoded") != NULL ) parse_get(&p,val_string(c->post_data)); } return p; } /** cgi_get_cwd : void -> string Return current bytecode file working directory **/ static value cgi_get_cwd() { mcontext *c = CONTEXT(); char *s = strrchr(c->r->filename,'/'); value v; char old; if( s != NULL ) { old = s[1]; s[1] = 0; } v = alloc_string(c->r->filename); if( s != NULL ) s[1] = old; return v; } /** cgi_set_main : function:0? -> void Set or disable the main entry point function **/ static value cgi_set_main( value f ) { if( val_is_null(f) ) { CONTEXT()->main = NULL; return val_true; } val_check_function(f,0); CONTEXT()->main = f; return val_true; } /** cgi_flush : void -> void Flush the data written so it's immediatly sent to the client **/ static value cgi_flush() { ap_rflush(CONTEXT()->r); return val_null; } /** cgi_get_config : void -> object Return the current configuration **/ #define FSET(name,t) alloc_field(v,val_id(#name),alloc_##t(c-> name)) static value cgi_get_config() { value v = alloc_object(NULL); mconfig *c = mod_neko_get_config(); FSET(hits,int); FSET(use_jit,bool); FSET(use_stats,bool); FSET(use_prim_stats,bool); FSET(use_cache,bool); FSET(exceptions,int); FSET(gc_period,int); FSET(max_post_size,int); return v; } /** cgi_set_config : object -> void Set the current configuration **/ #define FGET(name,t) f = val_field(v,val_id(#name)); val_check(f,t); c. name = val_##t(f) static value cgi_set_config( value v ) { mconfig c; value f; val_check(v,object); FGET(hits,int); FGET(use_jit,bool); FGET(use_stats,bool); FGET(use_prim_stats,bool); FGET(use_cache,bool); FGET(exceptions,int); FGET(gc_period,int); FGET(max_post_size,int); mod_neko_set_config(&c); return val_null; } /** cgi_command : any -> any Perform a configuration-specific command :
  • stats : returns the statistics
  • cache : returns the current cache
**/ extern value cgi_command( value v ); /** get_http_method : void -> string Returns the http method (GET,POST...) used by the client **/ static value get_http_method() { return alloc_string(CONTEXT()->r->method); } /** log_message : string -> void Write the message into the apache log **/ static value log_message( value message ) { mcontext *c = CONTEXT(); val_check(message, string); #ifdef APACHE_2_X ap_log_rerror(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, c->r, "[mod_neko] %s", val_string(message)); #else ap_log_rerror(APLOG_MARK, APLOG_NOTICE, c->r, "[mod_neko] %s", val_string(message)); #endif return val_null; } DEFINE_PRIM(cgi_get_cwd,0); DEFINE_PRIM(cgi_set_main,1); DEFINE_PRIM(get_cookies,0); DEFINE_PRIM(set_cookie,2); DEFINE_PRIM(get_host_name,0); DEFINE_PRIM(get_client_ip,0); DEFINE_PRIM(get_uri,0); DEFINE_PRIM(redirect,1); DEFINE_PRIM(get_params,0); DEFINE_PRIM(get_params_string,0); DEFINE_PRIM(get_post_data,0); DEFINE_PRIM(set_header,2); DEFINE_PRIM(set_return_code,1); DEFINE_PRIM(get_client_header,1); DEFINE_PRIM(get_client_headers,0); DEFINE_PRIM(parse_multipart_data,2); DEFINE_PRIM(cgi_flush,0); DEFINE_PRIM(cgi_get_config,0); DEFINE_PRIM(cgi_set_config,1); DEFINE_PRIM(cgi_command,1); DEFINE_PRIM(get_http_method,0); DEFINE_PRIM(log_message,1); /* ************************************************************************ */ neko-2-4-0/libs/mod_neko/mod_neko.c000066400000000000000000000317401464615675700171640ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "mod_neko.h" #include #include #include #ifndef MOD_NEKO_POST_SIZE # define MOD_NEKO_POST_SIZE (1 << 18) // 256 K #endif #ifdef APACHE_2_X # define FTIME(r) r->finfo.mtime # define ap_send_http_header(x) # define ap_soft_timeout(msg,r) # define ap_kill_timeout(r) # define ap_table_get apr_table_get # define LOG_SUCCESS APR_SUCCESS, typedef apr_time_t aptime; #else # define FTIME(r) r->finfo.st_mtime # define LOG_SUCCESS typedef time_t aptime; #endif #define apache_error(level,request,message) \ ap_rprintf(request,"Error : %s",message); \ ap_log_rerror(APLOG_MARK, level, LOG_SUCCESS request, "[mod_neko error] %s", message) typedef struct cache { value file; value main; int hits; aptime time; struct cache *next; } cache; static mconfig config; static int init_done = 0; static mt_local *cache_root = NULL; extern void neko_stats_measure( neko_vm *vm, const char *kind, int start ); extern value neko_stats_build( neko_vm *vm ); value cgi_command( value v ) { val_check(v,string); if( strcmp(val_string(v),"stats") == 0 ) return neko_stats_build(neko_vm_current()); if( strcmp(val_string(v),"cache") == 0 ) { cache *c = (cache*)local_get(cache_root); value l = val_null; while( c != NULL ) { value a = alloc_array(4); val_array_ptr(a)[0] = c->file; val_array_ptr(a)[1] = c->main; val_array_ptr(a)[2] = alloc_int(c->hits); val_array_ptr(a)[3] = l; l = a; c = c->next; } return l; } neko_error(); } mconfig *mod_neko_get_config() { return &config; } void mod_neko_set_config( mconfig *c ) { config = *c; } static void gc_major() { if( config.gc_period <= 0 || config.hits % config.gc_period != 0 ) return; if( config.use_stats ) neko_stats_measure(NULL,"gc",1); neko_gc_major(); if( config.use_stats ) neko_stats_measure(NULL,"gc",0); } static void send_headers( mcontext *c ) { if( !c->headers_sent ) { ap_send_http_header(c->r); c->headers_sent = true; } } static void request_print( const char *data, int size, void *_c ) { mcontext *c = (mcontext *)_c; if( c == NULL ) c = CONTEXT(); if( size == -1 ) size = (int)strlen(data); ap_soft_timeout("Client Timeout",c->r); send_headers(c); ap_rwrite(data,size,c->r); ap_kill_timeout(c->r); } static void null_print( const char *data, int size, void *_c ) { } static value cache_find( request_rec *r ) { cache *c = (cache*)local_get(cache_root); cache *prev = NULL; value fname = alloc_string(r->filename); while( c != NULL ) { if( val_compare(fname,c->file) == 0 ) { if( config.use_cache && FTIME(r) == c->time ) { c->hits++; return c->main; } if( prev == NULL ) local_set(cache_root,c->next); else prev->next = c->next; free_root((value*)c); // try to lower memory partitioning // when a module is updated c = NULL; gc_major(); break; } prev = c; c = c->next; } return NULL; } static char *request_base_uri( request_rec *r ) { while( r->prev != NULL ) r = r->prev; return r->unparsed_uri; } static void cache_module( const char *filename, aptime time, value main ) { cache *c = (cache*)local_get(cache_root), *prev = NULL; value fname = alloc_string(filename); while( c != NULL ) { if( val_compare(fname,c->file) == 0 ) { if( main == NULL ) { if( prev == NULL ) local_set(cache_root,c->next); else prev->next = c->next; free_root((value*)c); } else { c->main = main; c->time = time; } return; } prev = c; c = c->next; } c = (cache*)alloc_root(sizeof(struct cache) / sizeof(value)); c->file = fname; c->main = main; c->time = time; c->hits = 0; c->next = (cache*)local_get(cache_root); local_set(cache_root,c); } static int neko_handler_rec( request_rec *r ) { mcontext ctx; neko_vm *vm; const char *ctype; value exc = NULL; /* Seems to crash on Windows. And on Linux, we rarely have libGC 7.x installed anyway # if defined(APACHE_2_X) || defined(NEKO_WINDOWS) // we are using threads, so let's make sure that the current thread is registered neko_thread_register(true); # endif */ config.hits++; ctx.r = r; ctx.main = cache_find(r); ctx.post_data = val_null; ctx.headers_sent = false; ctx.content_type = alloc_string("text/html"); r->content_type = val_string(ctx.content_type); if( ap_setup_client_block(r,REQUEST_CHUNKED_ERROR) != 0 ) { send_headers(&ctx); apache_error(APLOG_WARNING,r,"ap_setup_client_block failed"); return OK; } ctype = ap_table_get(r->headers_in,"Content-Type"); if( (!ctype || strstr(ctype,"multipart/form-data") == NULL) && ap_should_client_block(r) ) { # define MAXLEN 1024 char buf[MAXLEN]; int len; int tlen = 0; buffer b = alloc_buffer(NULL); while( (len = ap_get_client_block(r,buf,MAXLEN)) > 0 ) { if( tlen < config.max_post_size ) buffer_append_sub(b,buf,len); tlen += len; } if( tlen >= config.max_post_size ) { send_headers(&ctx); apache_error(APLOG_WARNING,r,"Maximum POST data exceeded. Try using multipart encoding"); return OK; } ctx.post_data = buffer_to_string(b); } vm = neko_vm_alloc(NULL); if( config.use_stats ) neko_vm_set_stats(vm,neko_stats_measure,config.use_prim_stats?neko_stats_measure:NULL); neko_vm_set_custom(vm,k_mod_neko,&ctx); if( config.use_jit && !neko_vm_jit(vm,1) ) { send_headers(&ctx); apache_error(APLOG_WARNING,r,"JIT required by env. var but not enabled in NekoVM"); return OK; } neko_vm_redirect(vm,request_print,&ctx); neko_vm_select(vm); if( ctx.main != NULL ) { value old = ctx.main; if( config.use_stats ) neko_stats_measure(vm,r->filename,1); val_callEx(val_null,old,NULL,0,&exc); if( config.use_stats ) neko_stats_measure(vm,r->filename,0); if( old != ctx.main ) cache_module(r->filename,FTIME(r),ctx.main); } else { char *base_uri = request_base_uri(r); value mload = neko_default_loader(&base_uri,1); value args[] = { alloc_string(r->filename), mload }; char *p = strrchr(val_string(args[0]),'.'); if( p != NULL ) *p = 0; val_callEx(mload,val_field(mload,val_id("loadmodule")),args,2,&exc); if( ctx.main != NULL && config.use_cache ) cache_module(r->filename,FTIME(r),ctx.main); } if( exc != NULL ) { buffer b = alloc_buffer(NULL); value v; int i; const char *p, *start; value st = neko_exc_stack(vm); val_buffer(b,exc); config.exceptions++; ap_soft_timeout("Client Timeout",r); send_headers(&ctx); v = buffer_to_string(b); p = val_string(v); start = p; ap_rprintf(r,"Uncaught exception - "); while( *p ) { if( *p == '<' || *p == '>' ) { ap_rwrite(start,(int)(p - start),r); ap_rwrite((*p == '<')?"<":">",4, r); start = p + 1; } p++; } ap_rwrite(start,(int)(p - start),r); ap_rprintf(r,"

"); for(i=0;i"); else if( val_is_string(s) ) { ap_rprintf(r,"Called from %s (no debug available)
",val_string(s)); } else if( val_is_array(s) && val_array_size(s) == 2 && val_is_string(val_array_ptr(s)[0]) && val_is_int(val_array_ptr(s)[1]) ) ap_rprintf(r,"Called from %s line %d
",val_string(val_array_ptr(s)[0]),val_int(val_array_ptr(s)[1])); else { b = alloc_buffer(NULL); val_buffer(b,s); ap_rprintf(r,"Called from %s
",val_string(buffer_to_string(b))); } } ap_kill_timeout(r); return OK; } send_headers(&ctx); return OK; } static int neko_handler( request_rec *r ) { int ret; if( strcmp(r->handler,"neko-handler") != 0) return DECLINED; if( config.use_stats ) neko_stats_measure(NULL,r->hostname,1); ret = neko_handler_rec(r); neko_vm_select(NULL); if( config.use_stats ) neko_stats_measure(NULL,r->hostname,0); gc_major(); return ret; } static void mod_neko_do_init() { int tmp = 0; if( init_done ) return; init_done = 1; memset(&config,0,sizeof(config)); config.use_cache = 1; config.gc_period = 1; config.max_post_size = MOD_NEKO_POST_SIZE; # ifdef APACHE_2_X putenv(strdup("MOD_NEKO=2")); # else putenv(strdup("MOD_NEKO=1")); # endif neko_global_init(); cache_root = alloc_local(); } static value init_module() { neko_vm *vm = neko_vm_current(); mcontext *ctx = CONTEXT(); value env = vm->env; ctx->main = NULL; val_call1(val_array_ptr(env)[0],val_array_ptr(env)[1]); cache_module(ctx->r->filename,FTIME(ctx->r),ctx->main); return val_null; } static void preload_module( const char *name, server_rec *serv ) { value exc = NULL; neko_vm *vm = neko_vm_alloc(NULL); value mload = neko_default_loader(NULL,0); value m, read_path, exec; time_t time = 0; neko_vm_select(vm); if( config.use_jit ) neko_vm_jit(vm,1); if( !exc ) { value args[] = { alloc_string("std@module_read_path"), alloc_int(3) }; read_path = val_callEx(mload,val_field(mload,val_id("loadprim")),args,2,&exc); } if( !exc ) { value args[] = { alloc_string("std@module_exec"), alloc_int(1) }; exec = val_callEx(mload,val_field(mload,val_id("loadprim")),args,2,&exc); } if( !exc ) { value args[] = { val_null, alloc_string(name), mload }; char *p = strrchr(val_string(args[1]),'.'); if( p != NULL ) *p = 0; m = val_callEx(mload,read_path,args,3,&exc); } if( !exc ) { struct stat t; if( stat(name,&t) ) exc = alloc_string("failed to stat()"); else time = t.st_mtime; } if( !exc ) { value f = alloc_function(init_module,0,"init_module"); value env = alloc_array(2); val_array_ptr(env)[0] = exec; val_array_ptr(env)[1] = m; ((vfunction*)f)->env = env; cache_module(name,time,f); } if( exc ) { buffer b = alloc_buffer(NULL); val_buffer(b,exc); ap_log_error(APLOG_MARK,APLOG_WARNING,LOG_SUCCESS serv,"Failed to preload module '%s' : %s",name,val_string(buffer_to_string(b))); } neko_vm_select(NULL); } #ifdef APACHE_2_X # define MCONFIG void* #else # define MCONFIG char* #endif static const char *mod_neko_config( cmd_parms *cmd, MCONFIG mconfig, const char *fargs ) { char *code = strdup(fargs); char *args = code; int value; while( true ) { char c = *args; if( c == 0 || c == ' ' || c == '\t' ) break; args++; } while( *args == ' ' || *args == '\t' ) *args++ = 0; value = atoi(args); mod_neko_do_init(); if( strcmp(code,"JIT") == 0 ) config.use_jit = value; else if( strcmp(code,"CACHE") == 0 ) config.use_cache = value; else if( strcmp(code,"GC_PERIOD") == 0 ) config.gc_period = value; else if( strcmp(code,"POST_SIZE") == 0 ) config.max_post_size = value; else if( strcmp(code,"STATS") == 0 ) config.use_stats = value; else if( strcmp(code,"PRIM_STATS") == 0 ) config.use_prim_stats = value; else if( strcmp(code,"PRELOAD") == 0 ) preload_module(args,cmd->server); else ap_log_error(APLOG_MARK,APLOG_WARNING,LOG_SUCCESS cmd->server,"Unknown ModNeko configuration command '%s'",code); free(code); return NULL; } #ifdef APACHE_2_X static int neko_init( apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s ) { mod_neko_do_init(); return OK; } #else static void neko_init(server_rec *s, pool *p) { mod_neko_do_init(); } #endif static command_rec neko_module_cmds[] = { # ifdef APACHE_2_X AP_INIT_RAW_ARGS( "ModNeko", mod_neko_config , NULL, RSRC_CONF, NULL ), # else { "ModNeko", mod_neko_config, NULL, RSRC_CONF, RAW_ARGS, NULL }, # endif { NULL } }; #ifdef APACHE_2_X static void neko_register_hooks( apr_pool_t *p ) { ap_hook_post_config( neko_init, NULL, NULL, APR_HOOK_MIDDLE ); ap_hook_handler( neko_handler, NULL, NULL, APR_HOOK_LAST ); }; module AP_MODULE_DECLARE_DATA neko_module = { STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, neko_module_cmds, neko_register_hooks }; #else /* APACHE 1.3 */ static const handler_rec neko_handlers[] = { {"neko-handler", neko_handler}, {NULL} }; module MODULE_VAR_EXPORT neko_module = { STANDARD_MODULE_STUFF, neko_init, NULL, NULL, NULL, NULL, neko_module_cmds, neko_handlers, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #endif /* ************************************************************************ */ neko-2-4-0/libs/mod_neko/mod_neko.h000066400000000000000000000041221464615675700171630ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef MODNEKO_H #define MODNEKO_H #include #include #include #include #include #include #undef NOERROR #undef INLINE #include #ifndef NEKO_WINDOWS # include #endif typedef struct { request_rec *r; value main; value post_data; value content_type; bool headers_sent; } mcontext; typedef struct { int hits; int use_jit; int use_stats; int use_prim_stats; int use_cache; int exceptions; int gc_period; int max_post_size; } mconfig; #define CONTEXT() ((mcontext*)neko_vm_custom(neko_vm_current(),k_mod_neko)) DECLARE_KIND(k_mod_neko) #ifdef STANDARD20_MODULE_STUFF # define APACHE_2_X # define REMOTE_ADDR(c) c->remote_addr->sa.sin.sin_addr #else # define REMOTE_ADDR(c) c->remote_addr.sin_addr #endif extern mconfig *mod_neko_get_config(); extern void mod_neko_set_config( mconfig *cfg ); #endif /* ************************************************************************ */ neko-2-4-0/libs/mod_tora/000077500000000000000000000000001464615675700152315ustar00rootroot00000000000000neko-2-4-0/libs/mod_tora/CMakeLists.txt000066400000000000000000000013321464615675700177700ustar00rootroot00000000000000 ###################### # mod_tora2.ndll add_library(mod_tora2.ndll MODULE protocol.c mod_tora.c ) add_dependencies(mod_tora2.ndll mod_neko2.ndll socket ) target_include_directories(mod_tora2.ndll PRIVATE ${APACHE_INCLUDE_DIRS} ) target_link_libraries(mod_tora2.ndll socket ${APACHE_LIBRARIES} ) if (WIN32) target_link_libraries(mod_tora2.ndll ws2_32) endif() set_target_properties(mod_tora2.ndll PROPERTIES PREFIX "" OUTPUT_NAME mod_tora2 SUFFIX .ndll ) ####################### if(APPLE) set_target_properties(mod_tora2.ndll PROPERTIES LINK_FLAGS "-undefined dynamic_lookup ${LINK_FLAGS}" ) endif() install ( TARGETS mod_tora2.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/mod_tora/mod_tora.c000066400000000000000000000245011464615675700172030ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include "protocol.h" #ifndef OS_WINDOWS # include # define strcmpi strcasecmp #endif #define send_headers(c) \ if( !c->headers_sent ) { \ ap_send_http_header(c->r); \ c->headers_sent = 1; \ } #ifdef STANDARD20_MODULE_STUFF # define APACHE_2_X # define ap_send_http_header(x) # define ap_soft_timeout(msg,r) # define ap_kill_timeout(r) # define ap_table_get apr_table_get # define ap_table_set apr_table_set # define ap_table_add apr_table_add # define ap_table_do apr_table_do # define ap_palloc apr_palloc # define LOG_SUCCESS APR_SUCCESS, # define REDIRECT HTTP_MOVED_TEMPORARILY # define REMOTE_ADDR(c) c->remote_addr->sa.sin.sin_addr #else # define LOG_SUCCESS # define REMOTE_ADDR(c) c->remote_addr.sin_addr #endif #if AP_SERVER_MAJORVERSION_NUMBER >= 2 && AP_SERVER_MINORVERSION_NUMBER >= 4 # define REMOTE_IP(r) r->useragent_ip #else # define REMOTE_IP(r) r->connection->remote_ip #endif #define DEFAULT_HOST "127.0.0.1" #define DEFAULT_PORT 6666 #define DEFAULT_MAX_POST_DATA (1 << 18) // 256 K typedef struct { char *host; int port_min; int port_max; int max_post_size; int hits; bool proxy_mode; } mconfig; typedef struct { request_rec *r; proto *p; char *post_data; char *xff; char *client_ip; int post_data_size; bool headers_sent; bool is_multipart; bool is_form_post; bool need_discard; } mcontext; static mconfig config; static bool init_done = false; static int get_client_header( void *_c, const char *key, const char *val ) { mcontext *c = (mcontext*)_c; if( key == NULL || val == NULL ) return 1; if( config.proxy_mode && strcmpi(key,"X-Forwarded-For") == 0 ) protocol_send_header(c->p,key,c->xff); else protocol_send_header(c->p,key,val); return 1; } static void do_get_headers( void *_c ) { mcontext *c = (mcontext*)_c; ap_table_do(get_client_header,c,c->r->headers_in,NULL); } static void do_get_params( void *_c ) { mcontext *c = (mcontext*)_c; if( c->r->args ) protocol_send_raw_params(c->p,c->r->args); if( c->post_data && c->is_form_post ) protocol_send_raw_params(c->p,c->post_data); } static int do_print( void *_c, const char *buf, int len ) { mcontext *c = (mcontext*)_c; ap_soft_timeout("Client Timeout",c->r); send_headers(c); ap_rwrite(buf,len,c->r); ap_kill_timeout(c->r); return c->r->connection->aborted == 0; } static void do_flush( void *_c ) { mcontext *c = (mcontext*)_c; ap_rflush(c->r); } static void do_set_header( void *_c, const char *key, const char *value, bool add ) { mcontext *c = (mcontext*)_c; if( add ) ap_table_add(c->r->headers_out,key,value); else if( strcmpi(key,"Content-Type") == 0 ) { int len = (int)strlen(value); char *ct = (char*)ap_palloc(c->r->pool,len+1); memcpy(ct,value,len+1); c->r->content_type = ct; } else ap_table_set(c->r->headers_out,key,value); } static void do_set_return_code( void *_c, int code ) { mcontext *c = (mcontext*)_c; c->r->status = code; } static void do_log( void *_c, const char *msg, bool user_log ) { mcontext *c = (mcontext*)_c; if( user_log ) { c->r->content_type = "text/plain"; do_print(c,"Error : ",8); do_print(c,msg,(int)strlen(msg)); } else ap_log_rerror(APLOG_MARK, APLOG_WARNING, LOG_SUCCESS c->r, "[mod_tora] %s", msg); } static void log_error( mcontext *c, const char *msg ) { do_log(c,msg,false); // add to apache log do_log(c,msg,true); // display to user } static int do_stream_data( void *_c, char *buf, int size ) { mcontext *c = (mcontext*)_c; // startup if( size == 0 ) { if( !ap_should_client_block(c->r) ) return -1; c->need_discard = true; return 0; } return ap_get_client_block(c->r,buf,size); } static void discard_body( mcontext *c ) { char buf[1024]; while( ap_get_client_block(c->r,buf,1024) > 0 ) { } } static int tora_handler( request_rec *r ) { mcontext ctx, *c = &ctx; if( strcmp(r->handler,"tora-handler") != 0) return DECLINED; // init context c->need_discard = false; c->is_multipart = false; c->headers_sent = false; c->is_form_post = false; c->r = r; c->post_data = NULL; c->xff = NULL; c->client_ip = NULL; c->p = NULL; c->r->content_type = "text/html"; config.hits++; // read post data { const char *ctype = ap_table_get(r->headers_in,"Content-Type"); ap_setup_client_block(r,REQUEST_CHUNKED_ERROR); if( ctype && strstr(ctype,"multipart/form-data") ) c->is_multipart = true; else if( ap_should_client_block(r) ) { int tlen = 0; c->post_data = (char*)malloc(config.max_post_size); while( true ) { int len = ap_get_client_block(r,c->post_data + tlen,config.max_post_size - tlen); if( len <= 0 ) break; tlen += len; } if( tlen >= config.max_post_size ) { discard_body(c); free(c->post_data); log_error(c,"Maximum POST data exceeded. Try using multipart encoding"); return OK; } c->post_data[tlen] = 0; c->post_data_size = tlen; c->is_form_post = ctype == NULL || (strstr(ctype,"urlencoded") != NULL); } } // init protocol { protocol_infos infos; request_rec *first = r; while( first->prev != NULL ) first = first->prev; infos.custom = c; infos.script = r->filename; infos.uri = first->uri; infos.hostname = r->hostname ? r->hostname : ""; if( config.proxy_mode ) { const char *xff = ap_table_get(r->headers_in,"X-Forwarded-For"); if( xff == NULL ) infos.client_ip = REMOTE_IP(r); else { char tmp; char *xend = (char*)xff + strlen(xff) - 1; while( xend > xff && *xend != ' ' && *xend != ',' ) xend--; c->client_ip = strdup(xend); infos.client_ip = c->client_ip; if( xend > xff && *xend == ' ' && xend[-1] == ',' ) xend--; tmp = *xend; *xend = 0; c->xff = strdup(xff); *xend = tmp; } } else infos.client_ip = REMOTE_IP(r); infos.http_method = r->method; infos.get_data = r->args; infos.post_data = c->post_data; infos.post_data_size = c->post_data_size; infos.content_type = ap_table_get(r->headers_in,"Content-Type"); infos.do_get_headers = do_get_headers; infos.do_get_params = do_get_params; infos.do_set_header = do_set_header; infos.do_set_return_code = do_set_return_code; infos.do_print = do_print; infos.do_flush = do_flush; infos.do_log = do_log; infos.do_stream_data = c->is_multipart ? do_stream_data : NULL; c->p = protocol_init(&infos); } // run protocol { int port = config.port_min + (config.hits % (1 + config.port_max - config.port_min)); if( !protocol_connect(c->p,config.host,port) || !protocol_send_request(c->p) || !protocol_read_answer(c->p) ) log_error(c,protocol_get_error(c->p)); } // cleanup protocol_free(c->p); free(c->xff); free(c->client_ip); free(c->post_data); send_headers(c); // in case... if( c->need_discard ) discard_body(c); return OK; } static void mod_tora_do_init() { int tmp = 0; if( init_done ) return; init_done = true; memset(&config,0,sizeof(config)); config.host = DEFAULT_HOST; config.port_min = DEFAULT_PORT; config.port_max = DEFAULT_PORT; config.max_post_size = DEFAULT_MAX_POST_DATA; } #ifdef APACHE_2_X # define MCONFIG void* #else # define MCONFIG char* #endif static const char *mod_tora_config( cmd_parms *cmd, MCONFIG mconfig, const char *fargs ) { char *code = strdup(fargs); char *args = code; int value; while( true ) { char c = *args; if( c == 0 || c == ' ' || c == '\t' ) break; args++; } while( *args == ' ' || *args == '\t' ) *args++ = 0; value = atoi(args); mod_tora_do_init(); if( strcmp(code,"HOST") == 0 ) config.host = strdup(args); else if( strcmp(code,"PORT") == 0 ) { config.port_min = value; config.port_max = value; } else if( strcmp(code,"PORT_MAX") == 0 ) config.port_max = value; else if( strcmp(code,"POST_SIZE") == 0 ) config.max_post_size = value; else if( strcmp(code,"PROXY_MODE") == 0 ) config.proxy_mode = value; else ap_log_error(APLOG_MARK,APLOG_WARNING,LOG_SUCCESS cmd->server,"Unknown ModTora configuration command '%s'",code); free(code); return NULL; } #ifdef APACHE_2_X static int tora_init( apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s ) { mod_tora_do_init(); return OK; } #else static void tora_init(server_rec *s, pool *p) { mod_tora_do_init(); } #endif static command_rec tora_module_cmds[] = { # ifdef APACHE_2_X AP_INIT_RAW_ARGS( "ModTora", mod_tora_config , NULL, RSRC_CONF, NULL ), # else { "ModTora", mod_tora_config, NULL, RSRC_CONF, RAW_ARGS, NULL }, # endif { NULL } }; #ifdef APACHE_2_X static void tora_register_hooks( apr_pool_t *p ) { ap_hook_post_config( tora_init, NULL, NULL, APR_HOOK_MIDDLE ); ap_hook_handler( tora_handler, NULL, NULL, APR_HOOK_LAST ); }; module AP_MODULE_DECLARE_DATA tora_module = { STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, tora_module_cmds, tora_register_hooks }; #else /* APACHE 1.3 */ static const handler_rec tora_handlers[] = { {"tora-handler", tora_handler}, {NULL} }; module MODULE_VAR_EXPORT tora_module = { STANDARD_MODULE_STUFF, tora_init, NULL, NULL, NULL, NULL, tora_module_cmds, tora_handlers, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #endif /* ************************************************************************ */ neko-2-4-0/libs/mod_tora/protocol.c000066400000000000000000000275021464615675700172440ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include "protocol.h" #include "socket.h" struct _protocol { PSOCK s; bool error; char *error_msg; protocol_infos inf; }; typedef enum { CODE_FILE = 1, CODE_URI, CODE_CLIENT_IP, CODE_GET_PARAMS, CODE_POST_DATA, CODE_HEADER_KEY, CODE_HEADER_VALUE, CODE_HEADER_ADD_VALUE, CODE_PARAM_KEY, CODE_PARAM_VALUE, CODE_HOST_NAME, CODE_HTTP_METHOD, CODE_EXECUTE, CODE_ERROR, CODE_PRINT, CODE_LOG, CODE_FLUSH, CODE_REDIRECT, CODE_RETURNCODE, CODE_QUERY_MULTIPART, CODE_PART_FILENAME, CODE_PART_KEY, CODE_PART_DATA, CODE_PART_DONE, CODE_TEST_CONNECT, CODE_LISTEN, } proto_code; #define PARSE_HEADER(start,cursor) \ cursor = start; \ if( *cursor == '"' ) { \ start++; \ cursor++; \ while( *cursor != '"' && *cursor != 0 ) \ cursor++; \ } else { \ while( *cursor != 0 && *cursor != '\r' && *cursor != '\n' && *cursor != '\t' ) \ cursor++; \ } static bool proto_error( proto *p, const char *error ) { free(p->error_msg); p->error_msg = strdup(error); p->error = true; return false; } proto *protocol_init( protocol_infos *inf ) { proto *p = (proto*)malloc(sizeof(struct _protocol)); p->s = INVALID_SOCKET; p->inf = *inf; p->error = false; p->error_msg = NULL; psock_init(); return p; } bool protocol_connect( proto *p, const char *host, int port ) { PHOST h = phost_resolve(host); if( h == UNRESOLVED_HOST ) return proto_error(p,"Failed to resolve host"); p->s = psock_create(); if( p->s == INVALID_SOCKET ) return proto_error(p,"Failed to create socket"); if( psock_connect(p->s,h,port) != PS_OK ) return proto_error(p,"Failed to connect to TORA host"); return true; } const char *protocol_get_error( proto *p ) { return p->error_msg ? p->error_msg : "NO ERROR"; } void protocol_free( proto *p ) { psock_close(p->s); free(p->error_msg); free(p); } static void proto_write( proto *p, const char *str, int len ) { while( len ) { int b = psock_send(p->s,str,len); if( b <= 0 ) { p->error = true; return; } len -= b; str += b; } } static bool proto_read( proto *p, char *str, int len ) { while( len ) { int b = psock_recv(p->s,str,len); if( b <= 0 ) { p->error = true; return false; } len -= b; str += b; } return true; } static void proto_send_size( proto *p, proto_code code, const char *str, int len ) { unsigned char h[4]; h[0] = (unsigned char)code; h[1] = (unsigned char)len; h[2] = (unsigned char)(len >> 8); h[3] = (unsigned char)(len >> 16); proto_write(p,(char*)h,4); proto_write(p,str,len); } static void proto_send( proto *p, proto_code code, const char *str ) { proto_send_size(p,code,str,(int)strlen(str)); } void protocol_send_header( proto *p, const char *key, const char *val ) { proto_send(p,CODE_HEADER_KEY,key); proto_send(p,CODE_HEADER_VALUE,val); } static int url_decode( const char *bin, int len, char *bout ) { int pin = 0; int pout = 0; while( len-- > 0 ) { char c = bin[pin++]; if( c == '+' ) c = ' '; else if( c == '%' ) { int p1, p2; if( len < 2 ) break; p1 = bin[pin++]; p2 = bin[pin++]; len -= 2; if( p1 >= '0' && p1 <= '9' ) p1 -= '0'; else if( p1 >= 'a' && p1 <= 'f' ) p1 -= 'a' - 10; else if( p1 >= 'A' && p1 <= 'F' ) p1 -= 'A' - 10; else continue; if( p2 >= '0' && p2 <= '9' ) p2 -= '0'; else if( p2 >= 'a' && p2 <= 'f' ) p2 -= 'a' - 10; else if( p2 >= 'A' && p2 <= 'F' ) p2 -= 'A' - 10; else continue; c = (char)((unsigned char)((p1 << 4) + p2)); } bout[pout++] = c; } bout[pout] = 0; return pout; } #define DEFAULT_SIZE 256 static void proto_send_decode( proto *p, proto_code code, const char *str, int len ) { char tmp[DEFAULT_SIZE]; char *buf = NULL; int size; if( len >= DEFAULT_SIZE ) buf = malloc(len+1); size = url_decode(str,len,buf?buf:tmp); proto_send_size(p,code,buf?buf:tmp,size); if( buf ) free(buf); } void protocol_send_param( proto *p, const char *param, int param_size, const char *value, int value_size ) { proto_send_size(p,CODE_PARAM_KEY,param,param_size); proto_send_size(p,CODE_PARAM_VALUE,value,value_size); } void protocol_send_raw_params( proto *p, const char *args ) { char *aand, *aeq, *asep; while( true ) { aand = strchr(args,'&'); if( aand == NULL ) { asep = strchr(args,';'); aand = asep; } else { asep = strchr(args,';'); if( asep != NULL && asep < aand ) aand = asep; } if( aand != NULL ) *aand = 0; aeq = strchr(args,'='); if( aeq != NULL ) { *aeq = 0; proto_send_decode(p,CODE_PARAM_KEY,args,(int)(aeq-args)); proto_send_decode(p,CODE_PARAM_VALUE,aeq+1,(int)strlen(aeq+1)); *aeq = '='; } if( aand == NULL ) break; *aand = (aand == asep)?';':'&'; args = aand+1; } } static char *memfind( char *mem, int mlen, const char *v ) { char *found; int len = (int)strlen(v); if( len == 0 ) return mem; while( (found = memchr(mem,*v,mlen)) != NULL ) { if( (int)(found - mem) + len > mlen ) break; if( memcmp(found,v,len) == 0 ) return found; mlen -= (int)(found - mem + 1); mem = found + 1; } return NULL; } bool protocol_send_request( proto *p ) { proto_send(p,CODE_FILE,p->inf.script); proto_send(p,CODE_URI,p->inf.uri); proto_send(p,CODE_HOST_NAME,p->inf.hostname); proto_send(p,CODE_CLIENT_IP,p->inf.client_ip); if( p->inf.do_get_headers ) p->inf.do_get_headers(p->inf.custom); if( p->inf.get_data ) proto_send(p,CODE_GET_PARAMS,p->inf.get_data); if( p->inf.post_data ) proto_send_size(p,CODE_POST_DATA,p->inf.post_data,p->inf.post_data_size); if( p->inf.do_get_params ) p->inf.do_get_params(p->inf.custom); proto_send(p,CODE_HTTP_METHOD,p->inf.http_method); psock_set_fastsend(p->s,1); proto_send_size(p,CODE_EXECUTE,NULL,0); psock_set_fastsend(p->s,0); return !p->error; } static int fill_buffer( proto *p, char *buf, int bufsize, int pos ) { while( pos < bufsize ) { int k = p->inf.do_stream_data(p->inf.custom,buf+pos,bufsize-pos); if( k <= 0 ) break; pos += k; } return pos; } static bool send_multipart_data( proto *p, char *buf, int bufsize ) { int len = 0; char *boundstr = NULL; int boundstr_len; if( p->inf.content_type == NULL || p->inf.do_stream_data == NULL ) return true; // extract boundary value { const char *boundary, *bend; if( (boundary = strstr(p->inf.content_type,"boundary=")) == NULL ) return false; boundary += 9; PARSE_HEADER(boundary,bend); len = (int)(bend - boundary); boundstr_len = len + 2; if( boundstr_len > bufsize / 2 ) return false; boundstr = (char*)malloc(boundstr_len + 1); boundstr[0] = '-'; boundstr[1] = '-'; boundstr[boundstr_len] = 0; memcpy(boundstr+2,boundary,len); } len = 0; // permit the server to start download if needed if( p->inf.do_stream_data(p->inf.custom,NULL,0) != 0 ) { free(boundstr); return false; } while( true ) { char *name, *end_name, *filename, *end_file_name, *data; int pos; // refill buffer // we assume here that the the whole multipart header can fit in the buffer len = fill_buffer(p,buf,bufsize,len); // is boundary at the beginning of buffer ? if( len < boundstr_len || memcmp(buf,boundstr,boundstr_len) != 0 ) { free(boundstr); return false; } name = memfind(buf,len,"Content-Disposition:"); if( name == NULL ) break; name = memfind(name,len - (int)(name - buf),"name="); if( name == NULL ) { free(boundstr); return false; } name += 5; PARSE_HEADER(name,end_name); data = memfind(end_name,len - (int)(end_name - buf),"\r\n\r\n"); if( data == NULL ) { free(boundstr); return false; } filename = memfind(name,(int)(data - name),"filename="); if( filename != NULL ) { filename += 9; PARSE_HEADER(filename,end_file_name); } data += 4; pos = (int)(data - buf); // send part name if( filename ) proto_send_size(p,CODE_PART_FILENAME,filename,(int)(end_file_name - filename)); proto_send_size(p,CODE_PART_KEY,name,(int)(end_name - name)); // read data while( true ) { const char *boundary; // recall buffer memmove(buf,buf+pos,len - pos); len -= pos; pos = 0; len = fill_buffer(p,buf,bufsize,len); // lookup bounds boundary = memfind(buf,len,boundstr); if( boundary == NULL ) { if( len == 0 ) { free(boundstr); return false; } // send as much buffer as possible to client if( len < bufsize ) pos = len; else pos = len - boundstr_len + 1; proto_send_size(p,CODE_PART_DATA,buf,pos); } else { // send remaining data pos = (int)(boundary - buf); proto_send_size(p,CODE_PART_DATA,buf,pos - 2); // recall memmove(buf,buf+pos,len - pos); len -= pos; break; } } proto_send_size(p,CODE_PART_DONE,"",0); } free(boundstr); return true; } #define BUFSIZE (1 << 16) // 64 KB #define ABORT(msg) { proto_error(p,msg); goto exit; } bool protocol_read_answer( proto *p ) { unsigned char header[4]; int len; char *buf = (char*)malloc(BUFSIZE), *key = NULL; int buflen = BUFSIZE; int listening = 0; while( true ) { if( !proto_read(p,header,4) ) ABORT("Connection Closed"); len = header[1] | (header[2] << 8) | (header[3] << 16); if( buflen <= len ) { while( buflen <= len ) buflen <<= 1; free(buf); buf = (char*)malloc(buflen); } if( !proto_read(p,buf,len) ) ABORT("Connection Closed"); buf[len] = 0; switch( *header ) { case CODE_HEADER_KEY: key = strdup(buf); break; case CODE_HEADER_VALUE: if( !key ) ABORT("Missing key"); p->inf.do_set_header(p->inf.custom,key,buf,false); free(key); key = NULL; break; case CODE_HEADER_ADD_VALUE: if( !key ) ABORT("Missing key"); p->inf.do_set_header(p->inf.custom,key,buf,true); free(key); key = NULL; break; case CODE_EXECUTE: goto exit; case CODE_ERROR: p->inf.do_log(p->inf.custom,buf,true); goto exit; case CODE_PRINT: if( !p->inf.do_print(p->inf.custom,buf,len) && listening ) goto exit; if( listening ) p->inf.do_flush(p->inf.custom); break; case CODE_LOG: p->inf.do_log(p->inf.custom,buf,false); break; case CODE_FLUSH: p->inf.do_flush(p->inf.custom); break; case CODE_REDIRECT: p->inf.do_set_header(p->inf.custom,"Location",buf,false); p->inf.do_set_return_code(p->inf.custom,302); break; case CODE_RETURNCODE: p->inf.do_set_return_code(p->inf.custom,atoi(buf)); break; case CODE_QUERY_MULTIPART: { int tmpsize = atoi(buf); char *tmp = (char*)malloc(tmpsize + 1); tmp[tmpsize] = 0; if( !send_multipart_data(p,tmp,tmpsize) ) { free(tmp); ABORT("Failed to send multipart data"); } free(tmp); proto_send(p,CODE_EXECUTE,""); } break; case CODE_LISTEN: listening = 1; p->inf.do_flush(p->inf.custom); break; default: ABORT("Unexpected code"); } } exit: free(key); free(buf); return !p->error; } /* ************************************************************************ */ neko-2-4-0/libs/mod_tora/protocol.h000066400000000000000000000052321464615675700172450ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef PROTOCOL_H #define PROTOCOL_H #include "osdef.h" typedef void (*pf_callback)( void *custom ); typedef int (*pf_print)( void *custom, const char *buffer, int size ); typedef void (*pf_set_header)( void *custom, const char *key, const char *value, bool add ); typedef void (*pf_set_code)( void *custom, int code ); typedef void (*pf_log)( void *custom, const char *message, bool inner_log ); typedef int (*pf_stream_data)( void *custom, char *buffer, int size ); typedef struct { const char *script; const char *uri; const char *hostname; const char *client_ip; const char *http_method; const char *get_data; const char *post_data; const char *content_type; int post_data_size; pf_callback do_get_headers; pf_callback do_get_params; pf_print do_print; pf_callback do_flush; pf_set_header do_set_header; pf_set_code do_set_return_code; pf_stream_data do_stream_data; pf_log do_log; void *custom; } protocol_infos; struct _protocol; typedef struct _protocol proto; proto *protocol_init( protocol_infos *inf ); bool protocol_connect( proto *p, const char *host, int port ); bool protocol_send_request( proto *p ); void protocol_send_header( proto *p, const char *header, const char *value ); void protocol_send_param( proto *p, const char *param, int param_size, const char *value, int value_size ); void protocol_send_raw_params( proto *p, const char *data ); bool protocol_read_answer( proto *p ); const char *protocol_get_error( proto *p ); void protocol_free( proto *p ); #endif /* ************************************************************************ */ neko-2-4-0/libs/mysql/000077500000000000000000000000001464615675700145725ustar00rootroot00000000000000neko-2-4-0/libs/mysql/CMakeLists.txt000066400000000000000000000124401464615675700173330ustar00rootroot00000000000000 ###################### # OpenSSL if (STATIC_OPENSSL) if (WIN32) # perl is needed to run the openssl Configure script... find_package(Perl REQUIRED) if (arch_64) set(openssl_target VC-WIN64A) else() set(openssl_target VC-WIN32) endif() set(OPENSSL_CONFIGURE_COMMAND ${PERL_EXECUTABLE} Configure ${openssl_target} no-asm) set(OPENSSL_MAKE_COMMAND nmake /S) else() if (APPLE) if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386") set(OPENSSL_CONFIGURE_COMMAND ./Configure darwin-i386-cc "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") set(OPENSSL_CONFIGURE_COMMAND ./Configure darwin64-x86_64-cc "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64") set(OPENSSL_CONFIGURE_COMMAND ./Configure darwin64-arm64-cc "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") endif() else() set(OPENSSL_CONFIGURE_COMMAND ./config) endif() set(OPENSSL_MAKE_COMMAND make) endif() set(OPENSSL_CONFIGS CONFIGURE_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/openssl && ${OPENSSL_CONFIGURE_COMMAND} no-tests no-shared --prefix=${CMAKE_BINARY_DIR}/libs/src/install-prefix --libdir=lib BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/openssl && ${OPENSSL_MAKE_COMMAND} INSTALL_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/openssl && ${OPENSSL_MAKE_COMMAND} install_sw ) if (WIN32) set(OPENSSL_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/libssl.lib ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/libcrypto.lib ) else() set(OPENSSL_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/libssl.a ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/libcrypto.a ) endif() ExternalProject_Add(OpenSSL ${EP_CONFIGS} URL https://www.openssl.org/source/openssl-3.0.8.tar.gz URL_HASH SHA256=6c13d2bf38fdf31eac3ce2a347073673f5d63263398f1f69d0df4a41253e4b3e ${OPENSSL_CONFIGS} BYPRODUCTS ${OPENSSL_LIBRARIES} SOURCE_DIR ${CMAKE_BINARY_DIR}/libs/src/openssl ) set_target_properties(OpenSSL PROPERTIES ${EP_PROPS}) # Download project for fat source archive add_dependencies(download_deps OpenSSL-download) endif() ###################### # mysql.ndll add_library(mysql.ndll MODULE mysql.c) if (STATIC_MARIADBCONNECTOR) if (STATIC_OPENSSL) set(OPENSSL_CONF -DOPENSSL_USE_STATIC_LIBS=ON -DOPENSSL_ROOT_DIR=${CMAKE_BINARY_DIR}/libs/src/install-prefix) set(OPENSSL_DEP OpenSSL) elseif() set(OPENSSL_CONF "") set(OPENSSL_DEP "") endif() if (STATIC_ZLIB) set(ZLIB_CONF -DWITH_EXTERNAL_ZLIB=ON -DZLIB_ROOT=${CMAKE_BINARY_DIR}/libs/src/install-prefix ) set(ZLIB_DEP Zlib) else() if (UNIX) set(ZLIB_CONF -DWITH_EXTERNAL_ZLIB=ON) endif() set(ZLIB_DEP "") endif() if (WIN32) set(MARIADB_CONNECTOR_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/MariaDBConnector-build/libmariadb/${CMAKE_CFG_INTDIR}/mariadbclient.lib ) else() set(MARIADB_CONNECTOR_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/MariaDBConnector-build/libmariadb/libmariadbclient.a ) endif() ExternalProject_Add(MariaDBConnector ${EP_CONFIGS} DEPENDS ${OPENSSL_DEP} ${ZLIB_DEP} URL https://downloads.mariadb.com/Connectors/c/connector-c-3.3.4/mariadb-connector-c-3.3.4-src.tar.gz URL_HASH SHA256=486e5fdf976a8e7fadf583ae912128655e013ac575fa79b2d1af0fb8827a78ed CMAKE_ARGS -Wno-dev -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -DWITH_SSL=OPENSSL ${OPENSSL_CONF} ${ZLIB_CONF} PATCH_COMMAND ${CMAKE_COMMAND} -Dmariadb_source=${CMAKE_BINARY_DIR}/libs/src/MariaDBConnector -P ${CMAKE_SOURCE_DIR}/cmake/patch_mariadb.cmake BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/MariaDBConnector-build && ${CMAKE_COMMAND} --build . --target mariadbclient --config ${CMAKE_CFG_INTDIR} INSTALL_COMMAND echo skip install BUILD_BYPRODUCTS ${MARIADB_CONNECTOR_LIBRARIES} ) set_target_properties(MariaDBConnector PROPERTIES ${EP_PROPS}) set(MARIADB_CONNECTOR_INCLUDE_DIR ${CMAKE_BINARY_DIR}/libs/src/MariaDBConnector/include ${CMAKE_BINARY_DIR}/libs/src/MariaDBConnector-build/include ) add_dependencies(mysql.ndll MariaDBConnector) # Download project for fat source archive add_dependencies(download_deps MariaDBConnector-download) else() find_package(MariaDBConnector REQUIRED) endif() target_include_directories(mysql.ndll PRIVATE ${MARIADB_CONNECTOR_INCLUDE_DIR} ) target_link_libraries(mysql.ndll libneko ${MARIADB_CONNECTOR_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES}) if (WIN32) target_link_libraries(mysql.ndll ws2_32 crypt32 shlwapi secur32) endif() set_target_properties(mysql.ndll PROPERTIES PREFIX "" OUTPUT_NAME mysql SUFFIX .ndll ) if(APPLE) set_target_properties(mysql.ndll PROPERTIES LINK_FLAGS "-undefined dynamic_lookup ${LINK_FLAGS}" ) endif() ###################### # mysql5.ndll add_library(mysql5.ndll MODULE my_proto/my_proto.c my_proto/my_api.c mysql.c ) target_include_directories(mysql5.ndll PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/my_proto ) target_link_libraries(mysql5.ndll socket sha1 libneko ) if (WIN32) target_link_libraries(mysql5.ndll ws2_32) endif() set_target_properties(mysql5.ndll PROPERTIES PREFIX "" OUTPUT_NAME mysql5 SUFFIX .ndll ) install ( TARGETS mysql.ndll mysql5.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/mysql/my_proto/000077500000000000000000000000001464615675700164425ustar00rootroot00000000000000neko-2-4-0/libs/mysql/my_proto/my_api.c000066400000000000000000000321731464615675700200720ustar00rootroot00000000000000/* * MYSQL 5.0 Protocol Implementation * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "my_proto.h" static void error( MYSQL *m, const char *err, const char *param ) { if( param ) { unsigned int max = MAX_ERR_SIZE - (strlen(err) + 3); if( strlen(param) > max ) { char *p2 = (char*)malloc(max + 1); memcpy(p2,param,max-3); p2[max - 3] = '.'; p2[max - 2] = '.'; p2[max - 1] = '.'; p2[max] = 0; sprintf(m->last_error,err,param); free(p2); return; } } sprintf(m->last_error,err,param); m->errcode = -1; } static void save_error( MYSQL *m, MYSQL_PACKET *p ) { int ecode; p->pos = 0; // seems like we sometimes get some FFFFFF sequences before // the actual error... do { if( myp_read_byte(p) != 0xFF ) { m->errcode = -1; error(m,"Failed to decode error",NULL); return; } ecode = myp_read_ui16(p); } while( ecode == 0xFFFF ); if( m->is41 && p->buf[p->pos] == '#' ) p->pos += 6; // skip sqlstate marker error(m,"%s",myp_read_string(p)); m->errcode = ecode; } static int myp_ok( MYSQL *m, int allow_others ) { int code; MYSQL_PACKET *p = &m->packet; if( !myp_read_packet(m,p) ) { error(m,"Failed to read packet",NULL); return 0; } code = myp_read_byte(p); if( code == 0x00 ) return 1; if( code == 0xFF ) save_error(m,p); else if( allow_others ) return 1; else error(m,"Invalid packet error",NULL); return 0; } static void myp_close( MYSQL *m ) { psock_close(m->s); m->s = INVALID_SOCKET; } MYSQL *mysql_init( void *unused ) { MYSQL *m = (MYSQL*)malloc(sizeof(struct _MYSQL)); psock_init(); memset(m,0,sizeof(struct _MYSQL)); m->s = INVALID_SOCKET; error(m,"NO ERROR",NULL); m->errcode = 0; m->last_field_count = -1; m->last_insert_id = -1; m->affected_rows = -1; return m; } MYSQL *mysql_real_connect( MYSQL *m, const char *host, const char *user, const char *pass, void *unused, int port, const char *socket, int options ) { PHOST h; char scramble_buf[21]; MYSQL_PACKET *p = &m->packet; int pcount = 1; if( socket && *socket ) { error(m,"Unix Socket connections are not supported",NULL); return NULL; } h = phost_resolve(host); if( h == UNRESOLVED_HOST ) { error(m,"Failed to resolve host '%s'",host); return NULL; } m->s = psock_create(); if( m->s == INVALID_SOCKET ) { error(m,"Failed to create socket",NULL); return NULL; } psock_set_fastsend(m->s,1); psock_set_timeout(m->s,50); // 50 seconds if( psock_connect(m->s,h,port) != PS_OK ) { myp_close(m); error(m,"Failed to connect on host '%s'",host); return NULL; } if( !myp_read_packet(m,p) ) { myp_close(m); error(m,"Failed to read handshake packet",NULL); return NULL; } // process handshake packet { char filler[13]; unsigned int len; m->infos.proto_version = myp_read_byte(p); // this seems like an error packet if( m->infos.proto_version == 0xFF ) { myp_close(m); save_error(m,p); return NULL; } m->infos.server_version = strdup(myp_read_string(p)); m->infos.thread_id = myp_read_int(p); myp_read(p,scramble_buf,8); myp_read_byte(p); // should be 0 m->infos.server_flags = myp_read_ui16(p); m->infos.server_charset = myp_read_byte(p); m->infos.server_status = myp_read_ui16(p); m->infos.server_flags |= myp_read_ui16(p) << 16; len = myp_read_byte(p); myp_read(p,filler,10); // try to disable 41 m->is41 = (m->infos.server_flags & FL_PROTOCOL_41) != 0; if( !p->error && m->is41 ) myp_read(p,scramble_buf + 8,13); if( p->pos != p->size ) myp_read_string(p); // 5.5+ if( p->error ) { myp_close(m); error(m,"Failed to decode server handshake",NULL); return NULL; } // fill answer packet { unsigned int flags = m->infos.server_flags; int max_packet_size = 0x01000000; SHA1_DIGEST hpass; char filler[23]; flags &= (FL_PROTOCOL_41 | FL_TRANSACTIONS | FL_SECURE_CONNECTION); myp_begin_packet(p,128); if( m->is41 ) { myp_write_int(p,flags); myp_write_int(p,max_packet_size); myp_write_byte(p,m->infos.server_charset); memset(filler,0,23); myp_write(p,filler,23); myp_write_string(p,user); if( *pass ) { myp_encrypt_password(pass,scramble_buf,hpass); myp_write_bin(p,SHA1_SIZE); myp_write(p,hpass,SHA1_SIZE); myp_write_byte(p,0); } else myp_write_bin(p,0); } else { myp_write_ui16(p,flags); // max_packet_size myp_write_byte(p,0xFF); myp_write_byte(p,0xFF); myp_write_byte(p,0xFF); myp_write_string(p,user); if( *pass ) { char hpass[SEED_LENGTH_323 + 1]; myp_encrypt_pass_323(pass,scramble_buf,hpass); hpass[SEED_LENGTH_323] = 0; myp_write(p,hpass,SEED_LENGTH_323 + 1); } else myp_write_bin(p,0); } } } // send connection packet send_cnx_packet: if( !myp_send_packet(m,p,&pcount) ) { myp_close(m); error(m,"Failed to send connection packet",NULL); return NULL; } // read answer packet if( !myp_read_packet(m,p) ) { myp_close(m); error(m,"Failed to read packet",NULL); return NULL; } // increase packet counter (because we read one packet) pcount++; // process answer { int code = myp_read_byte(p); switch( code ) { case 0: // OK packet break; case 0xFF: // ERROR myp_close(m); save_error(m,p); return NULL; case 0xFE: // EOF // we are asked to send old password authentification if( p->size == 1 ) { char hpass[SEED_LENGTH_323 + 1]; myp_encrypt_pass_323(pass,scramble_buf,hpass); hpass[SEED_LENGTH_323] = 0; myp_begin_packet(p,0); myp_write(p,hpass,SEED_LENGTH_323 + 1); goto send_cnx_packet; } // fallthrough default: myp_close(m); error(m,"Invalid packet error",NULL); return NULL; } } // we are connected, setup a longer timeout psock_set_timeout(m->s,18000); return m; } int mysql_select_db( MYSQL *m, const char *dbname ) { MYSQL_PACKET *p = &m->packet; int pcount = 0; myp_begin_packet(p,0); myp_write_byte(p,COM_INIT_DB); myp_write_string_eof(p,dbname); if( !myp_send_packet(m,p,&pcount) ) { error(m,"Failed to send packet",NULL); return -1; } return myp_ok(m,0) ? 0 : -1; } int mysql_real_query( MYSQL *m, const char *query, int qlength ) { MYSQL_PACKET *p = &m->packet; int pcount = 0; myp_begin_packet(p,0); myp_write_byte(p,COM_QUERY); myp_write(p,query,qlength); m->last_field_count = -1; m->affected_rows = -1; m->last_insert_id = -1; if( !myp_send_packet(m,p,&pcount) ) { error(m,"Failed to send packet",NULL); return -1; } if( !myp_ok(m,1) ) return -1; p->id = IS_QUERY; return 0; } static int do_store( MYSQL *m, MYSQL_RES *r ) { int i; MYSQL_PACKET *p = &m->packet; p->pos = 0; r->nfields = myp_read_bin(p); if( p->error ) return 0; r->fields = (MYSQL_FIELD*)malloc(sizeof(MYSQL_FIELD) * r->nfields); memset(r->fields,0,sizeof(MYSQL_FIELD) * r->nfields); for(i=0;infields;i++) { if( !myp_read_packet(m,p) ) return 0; { MYSQL_FIELD *f = r->fields + i; f->catalog = m->is41 ? myp_read_bin_str(p) : NULL; f->db = m->is41 ? myp_read_bin_str(p) : NULL; f->table = myp_read_bin_str(p); f->org_table = m->is41 ? myp_read_bin_str(p) : NULL; f->name = myp_read_bin_str(p); f->org_name = m->is41 ? myp_read_bin_str(p) : NULL; if( m->is41 ) myp_read_byte(p); f->charset = m->is41 ? myp_read_ui16(p) : 0x08; f->length = m->is41 ? myp_read_int(p) : myp_read_bin(p); f->type = m->is41 ? myp_read_byte(p) : myp_read_bin(p); f->flags = m->is41 ? myp_read_ui16(p) : myp_read_bin(p); f->decimals = myp_read_byte(p); if( m->is41 ) myp_read_byte(p); // should be 0 if( m->is41 ) myp_read_byte(p); // should be 0 if( p->error ) return 0; } } // first EOF packet if( !myp_read_packet(m,p) ) return 0; if( myp_read_byte(p) != 0xFE || p->size >= 9 ) return 0; // reset packet buffer (to prevent to store large buffer in row data) free(p->buf); p->buf = NULL; p->mem = 0; // datas while( 1 ) { if( !myp_read_packet(m,p) ) return 0; // EOF : end of datas if( (unsigned char)p->buf[0] == 0xFE && p->size < 9 ) break; // ERROR ? if( (unsigned char)p->buf[0] == 0xFF ) { save_error(m,p); return 0; } // allocate one more row if( r->row_count == r->memory_rows ) { MYSQL_ROW_DATA *rows; r->memory_rows = r->memory_rows ? (r->memory_rows << 1) : 1; rows = (MYSQL_ROW_DATA*)malloc(r->memory_rows * sizeof(MYSQL_ROW_DATA)); memcpy(rows,r->rows,r->row_count * sizeof(MYSQL_ROW_DATA)); free(r->rows); r->rows = rows; } // read row fields { MYSQL_ROW_DATA *current = r->rows + r->row_count++; int prev = 0; current->raw = p->buf; current->lengths = (unsigned long*)malloc(sizeof(unsigned long) * r->nfields); current->datas = (char**)malloc(sizeof(char*) * r->nfields); for(i=0;infields;i++) { int l = myp_read_bin(p); if( !p->error ) p->buf[prev] = 0; if( l == -1 ) { current->lengths[i] = 0; current->datas[i] = NULL; } else { current->lengths[i] = l; current->datas[i] = p->buf + p->pos; p->pos += l; } prev = p->pos; } if( !p->error ) p->buf[prev] = 0; } // the packet buffer as been stored, don't reuse it p->buf = NULL; p->mem = 0; if( p->error ) return 0; } return 1; } MYSQL_RES *mysql_store_result( MYSQL *m ) { MYSQL_RES *r; MYSQL_PACKET *p = &m->packet; if( p->id != IS_QUERY ) return NULL; // OK without result if( p->buf[0] == 0 ) { p->pos = 0; m->last_field_count = myp_read_byte(p); // 0 m->affected_rows = myp_read_bin(p); m->last_insert_id = myp_read_bin(p); return NULL; } r = (MYSQL_RES*)malloc(sizeof(struct _MYSQL_RES)); memset(r,0,sizeof(struct _MYSQL_RES)); m->errcode = 0; if( !do_store(m,r) ) { mysql_free_result(r); if( !m->errcode ) error(m,"Failure while storing result",NULL); return NULL; } m->last_field_count = r->nfields; return r; } int mysql_field_count( MYSQL *m ) { return m->last_field_count; } int mysql_affected_rows( MYSQL *m ) { return m->affected_rows; } int mysql_escape_string( MYSQL *m, char *sout, const char *sin, int length ) { return myp_escape_string(m->infos.server_charset,sout,sin,length); } const char *mysql_character_set_name( MYSQL *m ) { const char *name = myp_charset_name(m->infos.server_charset); if( name == NULL ) { static char tmp[512]; sprintf(tmp,"#%d",m->infos.server_charset); return tmp; } return name; } int mysql_real_escape_string( MYSQL *m, char *sout, const char *sin, int length ) { if( !myp_supported_charset(m->infos.server_charset) ) return -1; if( m->infos.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES ) return myp_escape_quotes(m->infos.server_charset,sout,sin,length); return myp_escape_string(m->infos.server_charset,sout,sin,length); } void mysql_close( MYSQL *m ) { myp_close(m); free(m->packet.buf); free(m->infos.server_version); free(m); } const char *mysql_error( MYSQL *m ) { return m->last_error; } // RESULTS API unsigned int mysql_num_rows( MYSQL_RES *r ) { return r->row_count; } int mysql_num_fields( MYSQL_RES *r ) { return r->nfields; } MYSQL_FIELD *mysql_fetch_fields( MYSQL_RES *r ) { return r->fields; } unsigned long *mysql_fetch_lengths( MYSQL_RES *r ) { return r->current ? r->current->lengths : NULL; } MYSQL_ROW mysql_fetch_row( MYSQL_RES * r ) { MYSQL_ROW_DATA *cur = r->current; if( cur == NULL ) cur = r->rows; else { // free the previous result, since we're done with it free(cur->datas); free(cur->lengths); free(cur->raw); cur->datas = NULL; cur->lengths = NULL; cur->raw = NULL; // next cur++; } if( cur >= r->rows + r->row_count ) { free(r->rows); r->rows = NULL; r->memory_rows = 0; cur = NULL; } r->current = cur; return cur ? cur->datas : NULL; } void mysql_free_result( MYSQL_RES *r ) { if( r->fields ) { int i; for(i=0;infields;i++) { MYSQL_FIELD *f = r->fields + i; free(f->catalog); free(f->db); free(f->table); free(f->org_table); free(f->name); free(f->org_name); } free(r->fields); } if( r->rows ) { int i; for(i=0;irow_count;i++) { MYSQL_ROW_DATA *row = r->rows + i; free(row->datas); free(row->lengths); free(row->raw); } free(r->rows); } free(r); } /* ************************************************************************ */ neko-2-4-0/libs/mysql/my_proto/my_proto.c000066400000000000000000000232071464615675700204620ustar00rootroot00000000000000/* * MYSQL 5.0 Protocol Implementation * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "my_proto.h" #define MAX_PACKET_LENGTH 0xFFFFFF int myp_recv( MYSQL *m, void *buf, int size ) { while( size ) { int len = psock_recv(m->s,(char*)buf,size); if( len <= 0 ) return size == 0 ? 1 : 0; buf = ((char*)buf) + len; size -= len; } return 1; } int myp_send( MYSQL *m, void *buf, int size ) { while( size ) { int len = psock_send(m->s,(char*)buf,size); if( len <= 0 ) return size == 0 ? 1 : 0; buf = ((char*)buf) + len; size -= len; } return 1; } int myp_read( MYSQL_PACKET *p, void *buf, int size ) { if( p->size - p->pos < size ) { p->error = 1; return 0; } memcpy(buf,p->buf + p->pos,size); p->pos += size; return 1; } unsigned char myp_read_byte( MYSQL_PACKET *p ) { unsigned char c; if( !myp_read(p,&c,1) ) return 0; return c; } unsigned short myp_read_ui16( MYSQL_PACKET *p ) { unsigned short i; if( !myp_read(p,&i,2) ) return 0; return i; } int myp_read_int( MYSQL_PACKET *p ) { int i; if( !myp_read(p,&i,4) ) return 0; return i; } int myp_read_bin( MYSQL_PACKET *p ) { int c = myp_read_byte(p); if( c <= 250 ) return c; if( c == 251 ) return -1; // NULL if( c == 252 ) return myp_read_ui16(p); if( c == 253 ) { c = 0; myp_read(p,&c,3); return c; } if( c == 254 ) return myp_read_int(p); p->error = 1; return 0; } const char *myp_read_string( MYSQL_PACKET *p ) { char *str; if( p->pos >= p->size ) { p->error = 1; return ""; } str = p->buf + p->pos; p->pos += strlen(str) + 1; return str; } char *myp_read_bin_str( MYSQL_PACKET *p ) { int size = myp_read_bin(p); char *str; if( size == -1 ) return NULL; if( p->error || p->pos + size > p->size ) { p->error = 1; return NULL; } str = (char*)malloc(size + 1); memcpy(str,p->buf + p->pos, size); str[size] = 0; p->pos += size; return str; } int myp_read_packet( MYSQL *m, MYSQL_PACKET *p ) { unsigned int psize; p->pos = 0; p->error = 0; if( !myp_recv(m,&psize,4) ) { p->error = 1; p->size = 0; return 0; } //p->id = (psize >> 24); psize &= 0xFFFFFF; p->size = psize; if( p->mem < (int)psize ) { free(p->buf); p->buf = (char*)malloc(psize + 1); p->mem = psize; } p->buf[psize] = 0; if( psize == 0 || !myp_recv(m,p->buf,psize) ) { p->error = 1; p->size = 0; p->buf[0] = 0; return 0; } return 1; } int myp_send_packet( MYSQL *m, MYSQL_PACKET *p, int *packet_counter ) { unsigned int header; char *buf = p->buf; int size = p->size; int next = 1; while( next ) { int psize; if( size >= MAX_PACKET_LENGTH ) psize = MAX_PACKET_LENGTH; else { psize = size; next = 0; } header = psize | (((*packet_counter)++) << 24); if( !myp_send(m,&header,4) || !myp_send(m,buf,psize) ) { p->error = 1; return 0; } buf += psize; size -= psize; } return 1; } void myp_begin_packet( MYSQL_PACKET *p, int minsize ) { if( p->mem < minsize ) { free(p->buf); p->buf = (char*)malloc(minsize + 1); p->mem = minsize; } p->error = 0; p->size = 0; } void myp_write( MYSQL_PACKET *p, const void *data, int size ) { if( p->size + size > p->mem ) { char *buf2; if( p->mem == 0 ) p->mem = 32; do { p->mem <<= 1; } while( p->size + size > p->mem ); buf2 = (char*)malloc(p->mem + 1); memcpy(buf2,p->buf,p->size); free(p->buf); p->buf = buf2; } memcpy( p->buf + p->size , data, size ); p->size += size; } void myp_write_byte( MYSQL_PACKET *p, int i ) { unsigned char c = (unsigned char)i; myp_write(p,&c,1); } void myp_write_ui16( MYSQL_PACKET *p, int i ) { unsigned short c = (unsigned char)i; myp_write(p,&c,2); } void myp_write_int( MYSQL_PACKET *p, int i ) { myp_write(p,&i,4); } void myp_write_string( MYSQL_PACKET *p, const char *str ) { myp_write(p,str,strlen(str) + 1); } void myp_write_string_eof( MYSQL_PACKET *p, const char *str ) { myp_write(p,str,strlen(str)); } void myp_write_bin( MYSQL_PACKET *p, int size ) { if( size <= 250 ) { unsigned char l = (unsigned char)size; myp_write(p,&l,1); } else if( size < 0x10000 ) { unsigned char c = 252; unsigned short l = (unsigned short)size; myp_write(p,&c,1); myp_write(p,&l,2); } else if( size < 0x1000000 ) { unsigned char c = 253; unsigned int l = (unsigned short)size; myp_write(p,&c,1); myp_write(p,&l,3); } else { unsigned char c = 254; myp_write(p,&c,1); myp_write(p,&size,4); } } void myp_crypt( unsigned char *out, const unsigned char *s1, const unsigned char *s2, unsigned int len ) { unsigned int i; for(i=0;imax_value = 0x3FFFFFFFL; r->max_value_dbl = (double)r->max_value; r->seed1 = seed1 % r->max_value ; r->seed2 = seed2 % r->max_value; } static double myp_rnd( rand_ctx *r ) { r->seed1 = (r->seed1 * 3 + r->seed2) % r->max_value; r->seed2 = (r->seed1 + r->seed2 + 33) % r->max_value; return (((double) r->seed1)/r->max_value_dbl); } static void hash_password( unsigned long *result, const char *password, int password_len ) { register unsigned long nr = 1345345333L, add = 7, nr2 = 0x12345671L; unsigned long tmp; const char *password_end = password + password_len; for(; password < password_end; password++) { if( *password == ' ' || *password == '\t' ) continue; tmp = (unsigned long)(unsigned char)*password; nr ^= (((nr & 63)+add)*tmp)+(nr << 8); nr2 += (nr2 << 8) ^ nr; add += tmp; } result[0] = nr & (((unsigned long) 1L << 31) -1L); result[1] = nr2 & (((unsigned long) 1L << 31) -1L); } void myp_encrypt_pass_323( const char *password, const char seed[SEED_LENGTH_323], char to[SEED_LENGTH_323] ) { rand_ctx r; unsigned long hash_pass[2], hash_seed[2]; char extra, *to_start = to; const char *seed_end = seed + SEED_LENGTH_323; hash_password(hash_pass,password,(unsigned int)strlen(password)); hash_password(hash_seed,seed,SEED_LENGTH_323); random_init(&r,hash_pass[0] ^ hash_seed[0],hash_pass[1] ^ hash_seed[1]); while( seed < seed_end ) { *to++ = (char)(floor(myp_rnd(&r)*31)+64); seed++; } extra= (char)(floor(myp_rnd(&r)*31)); while( to_start != to ) *(to_start++) ^= extra; } // defined in mysql/strings/ctype-*.c const char *myp_charset_name( int charset ) { switch( charset ) { case 8: case 31: case 47: return "latin1"; case 63: return "binary"; // 101+ : utf16 // 160+ : utf32 case 33: case 83: case 223: case 254: return "utf8"; case 45: case 46: return "utf8mb4"; // superset of utf8 with up to 4 bytes per-char default: if( charset >= 192 && charset <= 211 ) return "utf8"; if( charset >= 224 && charset <= 243 ) return "utf8mb4"; } return NULL; } int myp_supported_charset( int charset ) { return myp_charset_name(charset) != NULL; } int myp_escape_string( int charset, char *sout, const char *sin, int length ) { // this is safe for UTF8 as well since mysql protects against invalid UTF8 char injection const char *send = sin + length; char *sbegin = sout; while( sin != send ) { char c = *sin++; switch( c ) { case 0: *sout++ = '\\'; *sout++ = '0'; break; case '\n': *sout++ = '\\'; *sout++ = 'n'; break; case '\r': *sout++ = '\\'; *sout++ = 'r'; break; case '\\': *sout++ = '\\'; *sout++ = '\\'; break; case '\'': *sout++ = '\\'; *sout++ = '\''; break; case '"': *sout++ = '\\'; *sout++ = '"'; break; case '\032': *sout++ = '\\'; *sout++ = 'Z'; break; default: *sout++ = c; } } *sout = 0; return sout - sbegin; } int myp_escape_quotes( int charset, char *sout, const char *sin, int length ) { const char *send = sin + length; char *sbegin = sout; while( sin != send ) { char c = *sin++; *sout++ = c; if( c == '\'' ) *sout++ = c; } *sout = 0; return sout - sbegin; } /* ************************************************************************ */ neko-2-4-0/libs/mysql/my_proto/my_proto.h000066400000000000000000000121651464615675700204700ustar00rootroot00000000000000/* * MYSQL 5.0 Protocol Implementation * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef MY_PROTO_H #define MY_PROTO_H #include "mysql.h" #include "socket.h" #include "sha1.h" typedef enum { FL_LONG_PASSWORD = 1, FL_FOUND_ROWS = 2, FL_LONG_FLAG = 4, FL_CONNECT_WITH_DB = 8, FL_NO_SCHEMA = 16, FL_COMPRESS = 32, FL_ODBC = 64, FL_LOCAL_FILES = 128, FL_IGNORE_SPACE = 256, FL_PROTOCOL_41 = 512, FL_INTERACTIVE = 1024, FL_SSL = 2048, FL_IGNORE_SIGPIPE = 4096, FL_TRANSACTIONS = 8192, FL_RESERVED = 16384, FL_SECURE_CONNECTION = 32768, FL_MULTI_STATEMENTS = 65536, FL_MULTI_RESULTS = 131072, } MYSQL_FLAG; typedef enum { COM_SLEEP = 0x00, COM_QUIT = 0x01, COM_INIT_DB = 0x02, COM_QUERY = 0x03, COM_FIELD_LIST = 0x04, //COM_CREATE_DB = 0x05, //COM_DROP_DB = 0x06 COM_REFRESH = 0x07, COM_SHUTDOWN = 0x08, COM_STATISTICS = 0x09, COM_PROCESS_INFO = 0x0A, //COM_CONNECT = 0x0B, COM_PROCESS_KILL = 0x0C, COM_DEBUG = 0x0D, COM_PING = 0x0E, //COM_TIME = 0x0F, //COM_DELAYED_INSERT = 0x10, COM_CHANGE_USER = 0x11, COM_BINLOG_DUMP = 0x12, COM_TABLE_DUMP = 0x13, COM_CONNECT_OUT = 0x14, COM_REGISTER_SLAVE = 0x15, COM_STMT_PREPARE = 0x16, COM_STMT_EXECUTE = 0x17, COM_STMT_SEND_LONG_DATA = 0x18, COM_STMT_CLOSE = 0x19, COM_STMT_RESET = 0x1A, COM_SET_OPTION = 0x1B, COM_STMT_FETCH = 0x1C, } MYSQL_COMMAND; typedef enum { SERVER_STATUS_IN_TRANS = 1, SERVER_STATUS_AUTOCOMMIT = 2, SERVER_MORE_RESULTS_EXISTS = 8, SERVER_QUERY_NO_GOOD_INDEX_USED = 16, SERVER_QUERY_NO_INDEX_USED = 32, SERVER_STATUS_CURSOR_EXISTS = 64, SERVER_STATUS_LAST_ROW_SENT = 128, SERVER_STATUS_DB_DROPPED = 256, SERVER_STATUS_NO_BACKSLASH_ESCAPES = 512, } MYSQL_SERVER_STATUS; typedef struct { unsigned char proto_version; char *server_version; unsigned int thread_id; unsigned int server_flags; unsigned char server_charset; unsigned short server_status; } MYSQL_INFOS; typedef struct { int id; int error; int size; int pos; int mem; char *buf; } MYSQL_PACKET; #define MAX_ERR_SIZE 1024 #define IS_QUERY -123456 struct _MYSQL { PSOCK s; MYSQL_INFOS infos; MYSQL_PACKET packet; int is41; int errcode; int last_field_count; int affected_rows; int last_insert_id; char last_error[MAX_ERR_SIZE]; }; typedef struct { char *raw; unsigned long *lengths; char **datas; } MYSQL_ROW_DATA; struct _MYSQL_RES { int nfields; MYSQL_FIELD *fields; MYSQL_ROW_DATA *rows; MYSQL_ROW_DATA *current; int row_count; int memory_rows; }; // network int myp_recv( MYSQL *m, void *buf, int size ); int myp_send( MYSQL *m, void *buf, int size ); int myp_read_packet( MYSQL *m, MYSQL_PACKET *p ); int myp_send_packet( MYSQL *m, MYSQL_PACKET *p, int *packet_counter ); // packet read int myp_read( MYSQL_PACKET *p, void *buf, int size ); unsigned char myp_read_byte( MYSQL_PACKET *p ); unsigned short myp_read_ui16( MYSQL_PACKET *p ); int myp_read_int( MYSQL_PACKET *p ); const char *myp_read_string( MYSQL_PACKET *p ); int myp_read_bin( MYSQL_PACKET *p ); char *myp_read_bin_str( MYSQL_PACKET *p ); // packet write void myp_begin_packet( MYSQL_PACKET *p, int minsize ); void myp_write( MYSQL_PACKET *p, const void *data, int size ); void myp_write_byte( MYSQL_PACKET *p, int b ); void myp_write_ui16( MYSQL_PACKET *p, int b ); void myp_write_int( MYSQL_PACKET *p, int b ); void myp_write_string( MYSQL_PACKET *p, const char *str ); void myp_write_string_eof( MYSQL_PACKET *p, const char *str ); void myp_write_bin( MYSQL_PACKET *p, int size ); // passwords #define SEED_LENGTH_323 8 void myp_crypt( unsigned char *out, const unsigned char *s1, const unsigned char *s2, unsigned int len ); void myp_encrypt_password( const char *pass, const char *seed, SHA1_DIGEST out ); void myp_encrypt_pass_323( const char *pass, const char seed[SEED_LENGTH_323], char out[SEED_LENGTH_323] ); // escaping int myp_supported_charset( int charset ); const char *myp_charset_name( int charset ); int myp_escape_string( int charset, char *sout, const char *sin, int length ); int myp_escape_quotes( int charset, char *sout, const char *sin, int length ); #endif /* ************************************************************************ */ neko-2-4-0/libs/mysql/my_proto/mysql.h000066400000000000000000000103511464615675700177600ustar00rootroot00000000000000/* * MYSQL 5.0 Protocol Implementation * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef MYSQL_H #define MYSQL_H struct _MYSQL; struct _MYSQL_RES; typedef struct _MYSQL MYSQL; typedef struct _MYSQL_RES MYSQL_RES; typedef char **MYSQL_ROW; typedef enum enum_field_types { FIELD_TYPE_DECIMAL = 0x00, FIELD_TYPE_TINY = 0x01, FIELD_TYPE_SHORT = 0x02, FIELD_TYPE_LONG = 0x03, FIELD_TYPE_FLOAT = 0x04, FIELD_TYPE_DOUBLE = 0x05, FIELD_TYPE_NULL = 0x06, FIELD_TYPE_TIMESTAMP = 0x07, FIELD_TYPE_LONGLONG = 0x08, FIELD_TYPE_INT24 = 0x09, FIELD_TYPE_DATE = 0x0A, FIELD_TYPE_TIME = 0x0B, FIELD_TYPE_DATETIME = 0x0C, FIELD_TYPE_YEAR = 0x0D, FIELD_TYPE_NEWDATE = 0x0E, FIELD_TYPE_VARCHAR = 0x0F, FIELD_TYPE_BIT = 0x10, FIELD_TYPE_NEWDECIMAL = 0xF6, FIELD_TYPE_ENUM = 0xF7, FIELD_TYPE_SET = 0xF8, FIELD_TYPE_TINY_BLOB = 0xF9, FIELD_TYPE_MEDIUM_BLOB = 0xFA, FIELD_TYPE_LONG_BLOB = 0xFB, FIELD_TYPE_BLOB = 0xFC, FIELD_TYPE_VAR_STRING = 0xFD, FIELD_TYPE_STRING = 0xFE, FIELD_TYPE_GEOMETRY = 0xFF } FIELD_TYPE; typedef enum { NOT_NULL_FLAG = 1, PRI_KEY_FLAG = 2, UNIQUE_KEY_FLAG = 4, MULTIPLE_KEY_FLAG = 8, BLOB_FLAG = 16, UNSIGNED_FLAG = 32, ZEROFILL_FLAG = 64, BINARY_FLAG = 128, ENUM_FLAG = 256, AUTO_INCREMENT_FLAG = 512, TIMESTAMP_FLAG = 1024, SET_FLAG = 2048, NUM_FLAG = 32768, } __FIELD_FLAG; typedef struct { char *catalog; char *db; char *table; char *org_table; char *name; char *org_name; int charset; int length; int flags; int decimals; FIELD_TYPE type; } MYSQL_FIELD; #define mysql_init mp_init #define mysql_real_connect mp_real_connect #define mysql_select_db mp_select_db #define mysql_real_query mp_real_query #define mysql_store_result mp_store_result #define mysql_field_count mp_field_count #define mysql_affected_rows mp_affected_rows #define mysql_escape_string mp_escape_string #define mysql_real_escape_string mp_real_escape_string #define mysql_close mp_close #define mysql_error mp_error #define mysql_num_rows mp_num_rows #define mysql_num_fields mp_num_fields #define mysql_fetch_fields mp_fetch_fields #define mysql_fetch_lengths mp_fetch_lengths #define mysql_fetch_row mp_fetch_row #define mysql_free_result mp_free_result MYSQL *mysql_init( void * ); MYSQL *mysql_real_connect( MYSQL *m, const char *host, const char *user, const char *pass, void *unused, int port, const char *socket, int options ); int mysql_select_db( MYSQL *m, const char *dbname ); int mysql_real_query( MYSQL *m, const char *query, int qlength ); MYSQL_RES *mysql_store_result( MYSQL *m ); int mysql_field_count( MYSQL *m ); int mysql_affected_rows( MYSQL *m ); int mysql_escape_string( MYSQL *m, char *sout, const char *sin, int length ); int mysql_real_escape_string( MYSQL *m, char *sout, const char *sin, int length ); void mysql_close( MYSQL *m ); const char *mysql_error( MYSQL *m ); const char *mysql_character_set_name( MYSQL *m ); unsigned int mysql_num_rows( MYSQL_RES *r ); int mysql_num_fields( MYSQL_RES *r ); MYSQL_FIELD *mysql_fetch_fields( MYSQL_RES *r ); unsigned long *mysql_fetch_lengths( MYSQL_RES *r ); MYSQL_ROW mysql_fetch_row( MYSQL_RES * r ); void mysql_free_result( MYSQL_RES *r ); #endif /* ************************************************************************ */ neko-2-4-0/libs/mysql/mysql.c000066400000000000000000000334131464615675700161070ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include typedef int SOCKET; #include #include /**

MySQL

API to connect and use MySQL database

**/ #define CNX(o) ((connection*)val_data(o)) #define RESULT(o) ((result*)val_data(o)) typedef struct { MYSQL *m; value conv_date; value conv_bytes; value conv_string; } connection; DEFINE_KIND(k_connection); DEFINE_KIND(k_result); static void error( MYSQL *m, const char *msg ) { buffer b = alloc_buffer(msg); buffer_append(b," "); buffer_append(b,mysql_error(m)); bfailure(b); } // --------------------------------------------------------------- // Result /**

Result

**/ #undef CONV_FLOAT typedef enum { CONV_INT, CONV_STRING, CONV_FLOAT, CONV_BINARY, CONV_DATE, CONV_DATETIME, CONV_BOOL } CONV; typedef struct { MYSQL_RES *r; int nfields; CONV *fields_convs; field *fields_ids; MYSQL_ROW current; value conv_date; value conv_string; value conv_bytes; } result; static void free_result( value o ) { result *r = RESULT(o); mysql_free_result(r->r); } /** result_set_conv_date : 'result -> function:1 -> void Set the function that will convert a Date or DateTime string to the corresponding value. **/ static value result_set_conv_date( value o, value c ) { val_check_function(c,1); if( val_is_int(o) ) return val_true; val_check_kind(o,k_result); RESULT(o)->conv_date = c; return val_true; } /** result_get_length : 'result -> int Return the number of rows returned or affected **/ static value result_get_length( value o ) { if( val_is_int(o) ) return o; val_check_kind(o,k_result); return alloc_int( (int)mysql_num_rows(RESULT(o)->r) ); } /** result_get_nfields : 'result -> int Return the number of fields in a result row **/ static value result_get_nfields( value o ) { val_check_kind(o,k_result); return alloc_int(RESULT(o)->nfields); } /** result_get_fields_names : 'result -> string array Return the fields names corresponding results columns **/ static value result_get_fields_names( value o ) { result *r; value a; int k; MYSQL_FIELD *fields; val_check_kind(o,k_result); r = RESULT(o); fields = mysql_fetch_fields(r->r); a = alloc_array(r->nfields); for(k=0;knfields;k++) val_array_ptr(a)[k] = alloc_string(fields[k].name); return a; } /** result_next : 'result -> object? Return the next row if available. A row is represented as an object, which fields have been converted to the corresponding Neko value (int, float or string). For Date and DateTime you can specify your own conversion function using [result_set_conv_date]. By default they're returned as plain strings. Additionally, the TINYINT(1) will be converted to either true or false if equal to 0. **/ static value result_next( value o ) { result *r; unsigned long *lengths = NULL; MYSQL_ROW row; val_check_kind(o,k_result); r = RESULT(o); row = mysql_fetch_row(r->r); if( row == NULL ) return val_null; { int i; value cur = alloc_object(NULL); r->current = row; for(i=0;infields;i++) if( row[i] != NULL ) { value v; switch( r->fields_convs[i] ) { case CONV_INT: v = alloc_best_int(atoi(row[i])); break; case CONV_STRING: v = alloc_string(row[i]); if( r->conv_string != NULL ) v = val_call1(r->conv_string,v); break; case CONV_BOOL: v = alloc_bool( *row[i] != '0' ); break; case CONV_FLOAT: v = alloc_float(atof(row[i])); break; case CONV_BINARY: if( lengths == NULL ) { lengths = mysql_fetch_lengths(r->r); if( lengths == NULL ) val_throw(alloc_string("mysql_fetch_lengths")); } v = copy_string(row[i],lengths[i]); if( r->conv_bytes != NULL ) v = val_call1(r->conv_bytes,v); break; case CONV_DATE: if( r->conv_date == NULL ) v = alloc_string(row[i]); else { struct tm t; sscanf(row[i],"%4d-%2d-%2d",&t.tm_year,&t.tm_mon,&t.tm_mday); t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; t.tm_isdst = -1; t.tm_year -= 1900; t.tm_mon--; v = val_call1(r->conv_date,alloc_int32((int)mktime(&t))); } break; case CONV_DATETIME: if( r->conv_date == NULL ) v = alloc_string(row[i]); else { struct tm t; sscanf(row[i],"%4d-%2d-%2d %2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec); t.tm_isdst = -1; t.tm_year -= 1900; t.tm_mon--; v = val_call1(r->conv_date,alloc_int32((int)mktime(&t))); } break; default: v = val_null; break; } alloc_field(cur,r->fields_ids[i],v); } return cur; } } /** result_get : 'result -> n:int -> string Return the [n]th field of the current row **/ static value result_get( value o, value n ) { result *r; const char *s; val_check_kind(o,k_result); val_check(n,int); r = RESULT(o); if( val_int(n) < 0 || val_int(n) >= r->nfields ) neko_error(); if( !r->current ) { result_next(o); if( !r->current ) neko_error(); } s = r->current[val_int(n)]; return alloc_string( s?s:"" ); } /** result_get_int : 'result -> n:int -> int Return the [n]th field of the current row as an integer (or 0) **/ static value result_get_int( value o, value n ) { result *r; const char *s; val_check_kind(o,k_result); val_check(n,int); r = RESULT(o); if( val_int(n) < 0 || val_int(n) >= r->nfields ) neko_error(); if( !r->current ) { result_next(o); if( !r->current ) neko_error(); } s = r->current[val_int(n)]; return alloc_int( s?atoi(s):0 ); } /** result_get_float : 'result -> n:int -> float Return the [n]th field of the current row as a float (or 0) **/ static value result_get_float( value o, value n ) { result *r; const char *s; val_check_kind(o,k_result); val_check(n,int); r = RESULT(o); if( val_int(n) < 0 || val_int(n) >= r->nfields ) neko_error(); if( !r->current ) { result_next(o); if( !r->current ) neko_error(); } s = r->current[val_int(n)]; return alloc_float( s?atof(s):0 ); } static CONV convert_type( enum enum_field_types t, int flags, unsigned int length ) { // FIELD_TYPE_TIME // FIELD_TYPE_YEAR // FIELD_TYPE_NEWDATE // FIELD_TYPE_NEWDATE + 2: // 5.0 MYSQL_TYPE_BIT switch( t ) { case FIELD_TYPE_TINY: if( length == 1 ) return CONV_BOOL; case FIELD_TYPE_SHORT: case FIELD_TYPE_LONG: case FIELD_TYPE_INT24: return CONV_INT; case FIELD_TYPE_LONGLONG: case FIELD_TYPE_DECIMAL: case FIELD_TYPE_FLOAT: case FIELD_TYPE_DOUBLE: case 246: // 5.0 MYSQL_NEW_DECIMAL return CONV_FLOAT; case FIELD_TYPE_BLOB: case FIELD_TYPE_TINY_BLOB: case FIELD_TYPE_MEDIUM_BLOB: case FIELD_TYPE_LONG_BLOB: if( (flags & BINARY_FLAG) != 0 ) return CONV_BINARY; return CONV_STRING; case FIELD_TYPE_DATETIME: case FIELD_TYPE_TIMESTAMP: return CONV_DATETIME; case FIELD_TYPE_DATE: return CONV_DATE; case FIELD_TYPE_NULL: case FIELD_TYPE_ENUM: case FIELD_TYPE_SET: //case FIELD_TYPE_VAR_STRING: //case FIELD_TYPE_GEOMETRY: // 5.0 MYSQL_TYPE_VARCHAR default: if( (flags & BINARY_FLAG) != 0 ) return CONV_BINARY; return CONV_STRING; } } static value alloc_result( connection *c, MYSQL_RES *r ) { result *res = (result*)alloc(sizeof(result)); value o = alloc_abstract(k_result,res); int num_fields = mysql_num_fields(r); int i,j; MYSQL_FIELD *fields = mysql_fetch_fields(r); res->r = r; res->conv_date = c->conv_date; res->conv_bytes = c->conv_bytes; res->conv_string = c->conv_string; res->current = NULL; res->nfields = num_fields; res->fields_ids = (field*)alloc_private(sizeof(field)*num_fields); res->fields_convs = (CONV*)alloc_private(sizeof(CONV)*num_fields); for(i=0;ifields_ids[j] == id ) { buffer b = alloc_buffer("Error, same field ids for : "); buffer_append(b,fields[i].name); buffer_append(b,":"); val_buffer(b,alloc_int(i)); buffer_append(b," and "); buffer_append(b,fields[j].name); buffer_append(b,":"); val_buffer(b,alloc_int(j)); buffer_append(b,"."); bfailure(b); } } res->fields_ids[i] = id; res->fields_convs[i] = convert_type(fields[i].type,fields[i].flags,fields[i].length); } val_gc(o,free_result); return o; } // --------------------------------------------------------------- // Connection /**

Connection

**/ /** close : 'connection -> void Close the connection. Any subsequent operation will fail on it **/ static value close( value o ) { val_check_kind(o,k_connection); mysql_close(CNX(o)->m); val_data(o) = NULL; val_kind(o) = NULL; val_gc(o,NULL); return val_true; } /** select_db : 'connection -> string -> void Select the database **/ static value select_db( value o, value db ) { val_check_kind(o,k_connection); val_check(db,string); if( mysql_select_db(CNX(o)->m,val_string(db)) != 0 ) error(CNX(o)->m,"Failed to select database :"); return val_true; } /** request : 'connection -> string -> 'result Execute an SQL request. Exception on error **/ static value request( value o, value r ) { MYSQL_RES *res; connection *c; val_check_kind(o,k_connection); val_check(r,string); c = CNX(o); if( mysql_real_query(c->m,val_string(r),val_strlen(r)) != 0 ) error(c->m,val_string(r)); res = mysql_store_result(c->m); if( res == NULL ) { if( mysql_field_count(c->m) == 0 ) return alloc_int( (int)mysql_affected_rows(c->m) ); else error(c->m,val_string(r)); } return alloc_result(c,res); } /** escape : 'connection -> string -> string Escape the string for inserting into a SQL request **/ static value escape( value o, value s ) { int len; value sout; val_check_kind(o,k_connection); val_check(s,string); len = val_strlen(s) * 2; sout = alloc_empty_string(len); len = mysql_real_escape_string(CNX(o)->m,val_string(sout),val_string(s),val_strlen(s)); if( len < 0 ) { buffer b = alloc_buffer("Unsupported charset : "); buffer_append(b,mysql_character_set_name(CNX(o)->m)); bfailure(b); } val_set_length(sout,len); val_string(sout)[len] = 0; return sout; } /** set_conv_funs : 'connection -> function:1 -> function:1 -> function:1 -> void Set three wrapper methods to be be called when creating a string, a date, and binary data in results **/ static value set_conv_funs( value o, value fstring, value fdate, value fbytes ) { val_check_kind(o,k_connection); val_check_function(fstring,1); val_check_function(fdate,1); val_check_function(fbytes,1); CNX(o)->conv_string = fstring; CNX(o)->conv_date = fdate; CNX(o)->conv_bytes = fbytes; return val_null; } // --------------------------------------------------------------- // Sql static void free_connection( value o ) { mysql_close(CNX(o)->m); } /** connect : { host => string, port => int, user => string, pass => string, socket => string? } -> 'connection Connect to a database using the connection informations **/ static value connect_mysql( value params ) { value host, port, user, pass, socket; val_check(params,object); host = val_field(params,val_id("host")); port = val_field(params,val_id("port")); user = val_field(params,val_id("user")); pass = val_field(params,val_id("pass")); socket = val_field(params,val_id("socket")); val_check(host,string); val_check(port,int); val_check(user,string); val_check(pass,string); if( !val_is_string(socket) && !val_is_null(socket) ) neko_error(); { connection *c = (connection*)alloc(sizeof(connection)); value v; c->m = mysql_init(NULL); c->conv_string = NULL; c->conv_date = NULL; c->conv_bytes = NULL; if( mysql_real_connect(c->m,val_string(host),val_string(user),val_string(pass),NULL,val_int(port),val_is_null(socket)?NULL:val_string(socket),0) == NULL ) { buffer b = alloc_buffer("Failed to connect to mysql server : "); buffer_append(b,mysql_error(c->m)); mysql_close(c->m); bfailure(b); } v = alloc_abstract(k_connection,c); val_gc(v,free_connection); return v; } } // --------------------------------------------------------------- // Registers DEFINE_PRIM_WITH_NAME(connect_mysql,connect,1); DEFINE_PRIM(close,1); DEFINE_PRIM(request,2); DEFINE_PRIM(select_db,2); DEFINE_PRIM(escape,2); DEFINE_PRIM(result_get_length,1); DEFINE_PRIM(result_get_nfields,1); DEFINE_PRIM(result_get_fields_names,1); DEFINE_PRIM(result_next,1); DEFINE_PRIM(result_get,2); DEFINE_PRIM(result_get_int,2); DEFINE_PRIM(result_get_float,2); DEFINE_PRIM(result_set_conv_date,2); DEFINE_PRIM(set_conv_funs,4); /* ************************************************************************ */ neko-2-4-0/libs/ocaml/000077500000000000000000000000001464615675700145205ustar00rootroot00000000000000neko-2-4-0/libs/ocaml/binast.ml000066400000000000000000000122751464615675700163410ustar00rootroot00000000000000(* * Neko Binary AST for OCaml * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Nast type context = { ch : unit IO.output; mutable curfile : string; mutable curline : int; mutable scount : int; strings : (string,int) Hashtbl.t; } let b ctx n = IO.write_byte ctx.ch n let write_ui24 ctx n = IO.write_byte ctx.ch n; IO.write_byte ctx.ch (n lsr 8); IO.write_byte ctx.ch (n lsr 16) let write_string ctx s = try let x = ctx.scount - Hashtbl.find ctx.strings s in if x > 0xFF then raise Not_found; b ctx x; with Not_found -> Hashtbl.replace ctx.strings s ctx.scount; ctx.scount <- ctx.scount + 1; b ctx 0; IO.write_ui16 ctx.ch (String.length s); IO.nwrite ctx.ch s let write_constant ctx = function | True -> b ctx 0 | False -> b ctx 1 | Null -> b ctx 2 | This -> b ctx 3 | Int n -> if n >= 0 && n <= 0xFF then begin b ctx 4; b ctx n; end else begin b ctx 5; IO.write_i32 ctx.ch n; end | Float s -> b ctx 6; write_string ctx s | String s -> b ctx 7; write_string ctx s | Builtin s -> b ctx 8; write_string ctx s | Ident s -> b ctx 9; write_string ctx s let write_op ctx op = b ctx (match op with | "+" -> 0 | "-" -> 1 | "/" -> 2 | "*" -> 3 | "%" -> 4 | "<<" -> 5 | ">>" -> 6 | ">>>" -> 7 | "|" -> 8 | "&" -> 9 | "^" -> 10 | "==" -> 11 | "!=" -> 12 | ">" -> 13 | ">=" -> 14 | "<" -> 15 | "<=" -> 16 | "=" -> 17 | "&&" -> 18 | "||" -> 19 | "++=" -> 20 | "--=" -> 21 | "+=" -> 22 | "-=" -> 23 | "/=" -> 24 | "*=" -> 25 | "%=" -> 26 | "<<=" -> 27 | ">>=" -> 28 | ">>>=" -> 29 | "|=" -> 30 | "&=" -> 31 | "^=" -> 32 | op -> failwith ("Invalid neko ast op " ^ op)) let rec write_expr_opt ctx = function | None -> b ctx 0; | Some e -> b ctx 1; write_expr ctx e and write_expr ctx (e,p) = if p.psource <> ctx.curfile then begin b ctx 0; write_string ctx p.psource; write_ui24 ctx p.pline; ctx.curfile <- p.psource; ctx.curline <- p.pline; end else if p.pline <> ctx.curline then begin b ctx 1; write_ui24 ctx p.pline; ctx.curline <- p.pline; end; match e with | EConst c -> b ctx 2; write_constant ctx c | EBlock el -> let n = List.length el in if n <= 0xFF then begin b ctx 3; b ctx n; end else begin b ctx 4; write_ui24 ctx n; end; List.iter (write_expr ctx) el | EParenthesis e -> b ctx 5; write_expr ctx e; | EField (e,f) -> b ctx 6; write_expr ctx e; write_string ctx f; | ECall (e,el) -> let n = List.length el in if n <= 0xFF then begin b ctx 7; write_expr ctx e; b ctx n; end else begin b ctx 28; write_expr ctx e; write_ui24 ctx n; end; List.iter (write_expr ctx) el; | EArray (e1,e2) -> b ctx 8; write_expr ctx e1; write_expr ctx e2; | EVars vl -> b ctx 9; b ctx (List.length vl); List.iter (fun (v,e) -> write_string ctx v; write_expr_opt ctx e; ) vl; | EWhile (e1,e2,NormalWhile) -> b ctx 10; write_expr ctx e1; write_expr ctx e2; | EWhile (e1,e2,DoWhile) -> b ctx 11; write_expr ctx e1; write_expr ctx e2; | EIf (e1,e2,eo) -> b ctx 12; write_expr ctx e1; write_expr ctx e2; write_expr_opt ctx eo; | ETry (e1,v,e2) -> b ctx 13; write_expr ctx e1; write_string ctx v; write_expr ctx e2; | EFunction (pl,e) -> b ctx 14; b ctx (List.length pl); List.iter (write_string ctx) pl; write_expr ctx e; | EBinop (op,e1,e2) -> b ctx 15; write_op ctx op; write_expr ctx e1; write_expr ctx e2; | EReturn None -> b ctx 16; | EReturn (Some e) -> b ctx 17; write_expr ctx e; | EBreak None -> b ctx 18; | EBreak (Some e) -> b ctx 19; write_expr ctx e; | EContinue -> b ctx 20; | ENext (e1,e2) -> b ctx 21; write_expr ctx e1; write_expr ctx e2; | EObject fl -> let n = List.length fl in if n <= 0xFF then begin b ctx 22; b ctx n; end else begin b ctx 23; write_ui24 ctx n; end; List.iter (fun (f,e) -> write_string ctx f; write_expr ctx e; ) fl; | ELabel l -> b ctx 24; write_string ctx l; | ESwitch (e,cases,eo) -> let n = List.length cases in if n <= 0xFF then begin b ctx 25; b ctx n; end else begin b ctx 26; write_ui24 ctx n; end; write_expr ctx e; List.iter (fun (e1,e2) -> write_expr ctx e1; write_expr ctx e2; ) cases; write_expr_opt ctx eo; | ENeko s -> b ctx 27; write_ui24 ctx (String.length s); IO.nwrite ctx.ch s let write ch e = let ctx = { ch = ch; curfile = ""; curline = -1; scount = 0; strings = Hashtbl.create 0; } in IO.nwrite ctx.ch "NBA\001"; write_expr ctx e neko-2-4-0/libs/ocaml/ml/000077500000000000000000000000001464615675700151305ustar00rootroot00000000000000neko-2-4-0/libs/ocaml/ml/mlast.ml000066400000000000000000000111371464615675700166050ustar00rootroot00000000000000(* * NekoML Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) type pos = { pmin : int; pmax : int; pfile : string; } type constant = | Int of int | Char of char | Bool of bool | Float of string | String of string | Ident of string | Constr of string | Module of string list * constant type keyword = | Var | If | Else | Function | Try | Catch | Type | Match | Then | When | While | Exception type token = | Eof | Semicolon | Dot | Comma | Quote | BraceOpen | BraceClose | ParentOpen of bool | ParentClose | BracketOpen | BracketClose | Arrow | Vertical | StreamOpen | StreamClose | Const of constant | Keyword of keyword | Binop of string | Comment of string | CommentLine of string type type_path = | EType of type_path option * string list * string | EPoly of string | ETuple of type_path list | EArrow of type_path * type_path type type_decl = | EAbstract | EAlias of type_path | ERecord of (string * bool * type_path) list | EUnion of (string * type_path option) list type arg = | ATyped of arg * type_path | ANamed of string | ATuple of arg list type pattern_decl = | PIdent of string | PConst of constant | PTuple of pattern list | PRecord of (string * pattern) list | PConstr of string list * string * pattern option | PAlias of string * pattern | PTyped of pattern * type_path | PStream of stream_item list * int and pattern = pattern_decl * pos and stream_item = | SPattern of pattern | SExpr of string list * expr | SMagicExpr of pattern * int and expr_decl = | EConst of constant | EBlock of expr list | EField of expr * string | ECall of expr * expr list | EArray of expr * expr | EVar of (string * type_path option) list * expr | EIf of expr * expr * expr option | EFunction of bool * string option * arg list * expr * type_path option | EBinop of string * expr * expr | EUnop of string * expr | ETypeAnnot of expr * type_path | ETupleDecl of expr list | ETypeDecl of string list * string * type_decl | EErrorDecl of string * type_path option | ERecordDecl of (string * expr) list | EMatch of expr * (pattern list * expr option * expr) list | ETry of expr * (pattern list * expr option * expr) list | ETupleGet of expr * int | EApply of expr * expr list | EWhile of expr * expr and expr = expr_decl * pos let pos = snd let null_pos = { pmin = -1; pmax = -1; pfile = "" } let punion p p2 = { pfile = p.pfile; pmin = min p.pmin p2.pmin; pmax = max p.pmax p2.pmax; } let escape_char = function | '\n' -> "\\n" | '\t' -> "\\t" | '\r' -> "\\r" | '\\' -> "\\\\" | c -> if c >= '\032' && c <= '\126' then String.make 1 c else Printf.sprintf "\\%.3d" (int_of_char c) let escape s = let b = Buffer.create (String.length s) in for i = 0 to (String.length s) - 1 do Buffer.add_string b (escape_char s.[i]) done; Buffer.contents b let rec s_constant = function | Int i -> string_of_int i | Float s -> s | Bool b -> if b then "true" else "false" | Char c -> "'" ^ escape_char c ^ "\"" | String s -> "\"" ^ escape s ^ "\"" | Ident s -> s | Constr s -> s | Module (l,c) -> String.concat "." l ^ "." ^ s_constant c let s_path path n = match path with | [] -> n | _ -> String.concat "." path ^ "." ^ n let s_keyword = function | Var -> "var" | If -> "if" | Else -> "else" | Function -> "function" | Try -> "try" | Catch -> "catch" | Type -> "type" | Match -> "match" | Then -> "then" | When -> "when" | While -> "while" | Exception -> "exception" let s_token = function | Eof -> "" | Semicolon -> ";" | Dot -> "." | Comma -> "," | Quote -> "'" | BraceOpen -> "{" | BraceClose -> "}" | ParentOpen _ -> "(" | ParentClose -> ")" | BracketOpen -> "[" | BracketClose -> "]" | StreamOpen -> "[<" | StreamClose -> ">]" | Arrow -> "->" | Vertical -> "|" | Const c -> s_constant c | Keyword k -> s_keyword k | Binop s -> s | Comment s -> "/*" ^ s ^ "*/" | CommentLine s -> "//" ^ s neko-2-4-0/libs/ocaml/ml/mllexer.mll000066400000000000000000000152731464615675700173160ustar00rootroot00000000000000(* * NekoML Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) { open Mlast open Lexing type error_msg = | Invalid_character of char | Unterminated_string | Unclosed_comment | Invalid_escaped_character of int | Invalid_escape exception Error of error_msg * pos let error_msg = function | Invalid_character c when int_of_char c > 32 && int_of_char c < 128 -> Printf.sprintf "Invalid character '%c'" c | Invalid_character c -> Printf.sprintf "Invalid character 0x%.2X" (int_of_char c) | Unterminated_string -> "Unterminated string" | Unclosed_comment -> "Unclosed comment" | Invalid_escaped_character n -> Printf.sprintf "Invalid escaped character %d" n | Invalid_escape -> "Invalid escape sequence" let cur_file = ref "" let all_lines = Hashtbl.create 0 let lines = ref [] let buf = Buffer.create 100 let error e pos = raise (Error (e,{ pmin = pos; pmax = pos; pfile = !cur_file })) let keywords = let h = Hashtbl.create 3 in List.iter (fun k -> Hashtbl.add h (s_keyword k) k) [Var;If;Else;Function;Try;Catch;Type;Match;Then;When;While;Exception] ; h let init file = cur_file := file; lines := [] let save_lines() = Hashtbl.replace all_lines !cur_file !lines let save() = save_lines(); !cur_file let restore file = save_lines(); cur_file := file; lines := Hashtbl.find all_lines file let newline lexbuf = lines := (lexeme_end lexbuf) :: !lines let find_line p lines = let rec loop n delta = function | [] -> n , p - delta | lp :: l when lp > p -> n , p - delta | lp :: l -> loop (n+1) lp l in loop 1 0 lines let get_error_line p = let lines = List.rev (try Hashtbl.find all_lines p.pfile with Not_found -> []) in let l, _ = find_line p.pmin lines in l let get_error_pos printer p = if p.pmin = -1 then "(unknown)" else let lines = List.rev (try Hashtbl.find all_lines p.pfile with Not_found -> []) in let l1, p1 = find_line p.pmin lines in let l2, p2 = find_line p.pmax lines in if l1 = l2 then begin let s = (if p1 = p2 then Printf.sprintf " %d" p1 else Printf.sprintf "s %d-%d" p1 p2) in Printf.sprintf "%s character%s" (printer p.pfile l1) s end else Printf.sprintf "%s lines %d-%d" (printer p.pfile l1) l1 l2 let reset() = Buffer.reset buf let contents() = Buffer.contents buf let store lexbuf = Buffer.add_string buf (lexeme lexbuf) let add c = Buffer.add_string buf c let mk_tok t pmin pmax = t , { pfile = !cur_file; pmin = pmin; pmax = pmax } let mk lexbuf t = mk_tok t (lexeme_start lexbuf) (lexeme_end lexbuf) let mk_ident lexbuf = let s = lexeme lexbuf in mk lexbuf (try Keyword (Hashtbl.find keywords s) with Not_found -> Const (Ident s)) } let ident = ['a'-'z' '_'] ['a'-'z' 'A'-'Z' '0'-'9' '_']* let modident = ['A'-'Z'] ['a'-'z' 'A'-'Z' '0'-'9' '_']* let binop = ['!' '=' '*' '/' '<' '>' '&' '|' '^' '%' '+' ':' '-'] let number = ['0'-'9'] let space = [' ' '\r' '\t'] rule token = parse | eof { mk lexbuf Eof } | ';' { mk lexbuf Semicolon } | '.' { mk lexbuf Dot } | ',' { mk lexbuf Comma } | '{' { mk lexbuf BraceOpen } | '}' { mk lexbuf BraceClose } | space+ '(' { mk lexbuf (ParentOpen true) } | '(' { mk lexbuf (ParentOpen false) } | ')' { mk lexbuf ParentClose } | '[' { mk lexbuf BracketOpen } | ']' { mk lexbuf BracketClose } | '\'' { mk lexbuf Quote } | '|' { mk lexbuf Vertical } | space+ { token lexbuf } | '\n' { newline lexbuf; token lexbuf } | "0x" ['0'-'9' 'a'-'f' 'A'-'F']+ | number+ { mk lexbuf (Const (Int (int_of_string (lexeme lexbuf)))) } | number+ '.' number* | '.' number+ { mk lexbuf (Const (Float (lexeme lexbuf))) } | "true" { mk lexbuf (Const (Bool true)) } | "false" { mk lexbuf (Const (Bool false)) } | '"' { reset(); let pmin = lexeme_start lexbuf in let pmax = (try string lexbuf with Exit -> error Unterminated_string pmin) in mk_tok (Const (String (contents()))) pmin pmax; } | "/*" { reset(); let pmin = lexeme_start lexbuf in let pmax = (try comment lexbuf with Exit -> error Unclosed_comment pmin) in mk_tok (Comment (contents())) pmin pmax; } | "'\\n'" { mk lexbuf (Const (Char '\n')) } | "'\\t'" { mk lexbuf (Const (Char '\t')) } | "'\\r'" { mk lexbuf (Const (Char '\r')) } | "'\\''" { mk lexbuf (Const (Char '\'')) } | "'\\\\'" { mk lexbuf (Const (Char '\\')) } | "'\\\"'" | "'\\t'" { mk lexbuf (Const (Char '"')) } | '\'' [^'\\'] '\'' { mk lexbuf (Const (Char (lexeme lexbuf).[1])) } | "\'\\" ['0'-'9'] ['0'-'9'] ['0'-'9'] '\'' { let s = String.sub (lexeme lexbuf) 2 3 in let n = int_of_string s in let c = (try char_of_int n with _ -> error (Invalid_escaped_character n) (lexeme_start lexbuf)) in mk lexbuf (Const (Char c)) } | "//" [^'\n']* { let s = lexeme lexbuf in let n = (if s.[String.length s - 1] = '\r' then 3 else 2) in mk lexbuf (CommentLine (String.sub s 2 ((String.length s)-n))) } | "[<" { mk lexbuf StreamOpen } | ">]" { mk lexbuf StreamClose } | "->" { mk lexbuf Arrow } | binop binop? | ">>>" | "===" | "!==" | "or" | "and" | "xor" { mk lexbuf (Binop (lexeme lexbuf)) } | ident { mk_ident lexbuf } | modident { mk lexbuf (Const (Constr (lexeme lexbuf))) } | _ { error (Invalid_character (lexeme_char lexbuf 0)) (lexeme_start lexbuf) } and comment = parse | eof { raise Exit } | '\r' { comment lexbuf } | '\n' { newline lexbuf; store lexbuf; comment lexbuf } | "*/" { lexeme_end lexbuf } | '*' { store lexbuf; comment lexbuf } | [^'*' '\n' '\r']+ { store lexbuf; comment lexbuf } and string = parse | eof { raise Exit } | '\n' { newline lexbuf; store lexbuf; string lexbuf } | "\\\"" { add "\""; string lexbuf } | "\\\\" { add "\\"; string lexbuf } | "\\n" { add "\n"; string lexbuf } | "\\t" { add "\t"; string lexbuf } | "\\r" { add "\r"; string lexbuf } | '\\' ['0'-'9'] ['0'-'9'] ['0'-'9'] { let i = int_of_string (String.sub (lexeme lexbuf) 1 3) in if i >= 256 then error (Invalid_escaped_character i) (lexeme_start lexbuf); add (String.make 1 (char_of_int i)); string lexbuf } | '\\' { error Invalid_escape (lexeme_start lexbuf) } | '"' { lexeme_end lexbuf } | [^'"' '\\' '\n']+ { store lexbuf; string lexbuf } neko-2-4-0/libs/ocaml/ml/mlmain.ml000066400000000000000000000041611464615675700167410ustar00rootroot00000000000000(* * NekoML Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) let nekoml file ch out = IO.close_in ch; Mltyper.verbose := !Plugin.verbose; Mlneko.verbose := !Plugin.verbose; let path = ExtString.String.nsplit (Filename.dirname file) "/" in let modname = path @ [String.capitalize (Filename.chop_extension (Filename.basename file))] in let ctx = Mltyper.context (!Plugin.paths) in ignore(Mltyper.load_module ctx modname Mlast.null_pos); Hashtbl.iter (fun m (e,deps,idents) -> let e = Mlneko.generate e deps idents m in let file = String.concat "/" m ^ ".neko" in let ch = (if m = modname then out else IO.output_channel (open_out file)) in let ctx = Printer.create ch in Printer.print ctx e; IO.close_out ch ) (Mltyper.modules ctx) let nekoml_exn = function | Mllexer.Error (m,p) -> Plugin.exn_infos "syntax error" (Mllexer.error_msg m) (fun f -> Mllexer.get_error_pos f p) | Mlparser.Error (m,p) -> Plugin.exn_infos "parse error" (Mlparser.error_msg m) (fun f -> Mllexer.get_error_pos f p) | Mltyper.Error (m,p) -> Plugin.exn_infos "type error" (Mltyper.error_msg m) (fun f -> Mllexer.get_error_pos f p) | Lexer.Error (m,p) -> Plugin.exn_infos "syntax error" (Lexer.error_msg m) (fun f -> Lexer.get_error_pos f p) | Parser.Error (m,p) -> Plugin.exn_infos "parse error" (Parser.error_msg m) (fun f -> Lexer.get_error_pos f p) | e -> raise e ;; Plugin.register "nml" "neko" nekoml nekoml_exn neko-2-4-0/libs/ocaml/ml/mlmatch.ml000066400000000000000000000231121464615675700171060ustar00rootroot00000000000000(* * NekoML Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) (* ----- We're using the same algorithm and source code as described in chapter 5.2.4 of "The Zinc experiment" by X. Leroy ( https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.43.6772 ) also described in "The Implementation of Functional Programming Languages" by Simon Peyton Jones, in the Chapter 5 by Philip Wadler ( https://www.microsoft.com/en-us/research/publication/the-implementation-of-functional-programming-languages/ ) *) open Mlast open Mltype type complete = | Total | Partial | Dubious type lambda = match_op type matching = (pattern list * lambda) list * lambda list let fully_matched_ref = ref (fun cl -> false) let error_ref = ref (fun (msg : string) (p : pos) -> assert false; ()) let error msg p = !error_ref msg p; assert false let failure = MFailure let handle l1 l2 = if l2 = MFailure then l1 else if l1 = MFailure then l2 else MHandle (l1,l2) let exec e = MExecute (e,true) let cond path lambdas = MConstants (path,lambdas) let rec switch path lambdas = match lambdas with | [TVoid,m1] -> m1 | (TVoid,m1) :: l -> MNext (m1, switch path l) | _ -> MSwitch (path,lambdas) let ewhen e e2 = MWhen (e,e2) let rec bind v p = function | MBind (v2,p2,m) -> MBind (v2,p2,bind v p m) | act -> if v = "_" then act else MBind (v,p,act) let rec junk p k = function | MBind (v,m,m2) -> MBind (v,m,junk p k m2) | act -> if k = 0 then act else MJunk (p,k,act) let rec stream_pattern (p,pos) = (match p with | PIdent i -> PConst (Ident i) | PConst c -> PConst c | PTuple pl -> PTuple (List.map stream_pattern pl) | PRecord pr -> PRecord (List.map (fun (s,p) -> s , stream_pattern p) pr) | PConstr (path,name,param) -> PConstr (path,name,match param with None -> None | Some p -> Some (stream_pattern p)) | PAlias (s,p) -> PAlias (s,stream_pattern p) | PTyped (p,t) -> PTyped (stream_pattern p,t) | PStream (s,k) -> PStream (s,k)) , pos let rec have_when = function | MWhen _ -> true | MBind (_,_,e) -> have_when e | _ -> false let t_const = function | Int i -> TInt i | String s -> TString s | Float f -> TFloat f | Char c -> TChar c | Ident i -> TIdent i | Bool b -> TBool b | _ -> assert false let total p1 p2 = match p1 , p2 with | Total , Total -> Total | Partial , _ -> Partial | _ , Partial -> Partial | _ , _ -> Dubious let partial p1 p2 = match p1 , p2 with | Total , _ -> p2 | _ , Total -> Total | _ , _ -> Dubious let rec start_by_a_variable (p,_) = match p with | PAlias (_,p) -> start_by_a_variable p | PIdent _ -> true | _ -> false let add_to_match (casel,pathl) cas = cas :: casel , pathl let make_constant_match path cas = match path with | [] -> assert false | _ :: pathl -> [cas] , pathl let make_token_match path cas = [cas] , path let make_construct_match tuple nargs pathl cas = match pathl with | [] -> assert false | path :: pathl -> let rec make_path i = if i >= nargs then pathl else let k = if tuple then MTuple (path,i) else MField (path,i) in k :: make_path (i + 1) in [cas] , make_path 0 let make_record_match args pathl cas = match pathl with | [] -> assert false | path :: pathl -> [cas] , List.fold_left (fun acc (f,_) -> MRecordField (path,f) :: acc) pathl (List.rev args) let add_to_division make_match divlist key cas = try let matchref = List.assoc key divlist in matchref := add_to_match !matchref cas; divlist with Not_found -> (key , ref (make_match cas)) :: divlist let always_add make_match divlist cas = (TVoid , ref (make_match cas)) :: divlist let lines_of_matching = fst let fully_matched cl = !fully_matched_ref cl let flatten = function | None -> [] | Some (PTuple l,_) -> l | Some p -> [p] let split_matching (m:matching) = match m with | _ , [] -> assert false | casel, (curpath :: endpathl as pathl) -> let rec split_rec = function | ((PTyped (p,_),_) :: l , act) :: rest -> split_rec ((p :: l, act) :: rest) | ((PAlias (var,p),_) :: l , act) :: rest -> split_rec ((p :: l, bind var curpath act) :: rest) | ((PIdent var,_) :: l , act) :: rest -> let vars , others = split_rec rest in add_to_match vars (l, bind var curpath act) , others | casel -> ([] , endpathl) , (casel , pathl) in split_rec casel let divide_matching (m:matching) = match m with | _ , [] -> assert false | casel , (curpath :: tailpathl as pathl) -> let rec divide_rec = function | [] -> [] , [] , ([] , pathl) | ([],_) :: _ -> assert false | ((PTyped (p,_),_) :: l , act) :: rest -> divide_rec ((p :: l , act) :: rest) | ((PAlias (var,p),_) :: l, act) :: rest -> divide_rec ((p :: l , bind var curpath act) :: rest) | ((PConst c,_) :: l, act) :: rest -> let constant , constrs, others = divide_rec rest in add_to_division (make_constant_match pathl) constant (t_const c) (l, act), constrs , others | ((PConstr (path,c,arg),_) :: l,act) :: rest -> let constants , constrs, others = divide_rec rest in let args = flatten arg in constants , add_to_division (make_construct_match false (List.length args) pathl) constrs (TModule (path,TConstr c)) (args @ l,act) , others | ((PTuple [],_) :: l,act) :: rest -> let constants , constrs, others = divide_rec rest in constants , add_to_division (make_constant_match pathl) constrs TVoid (l, act), others | ((PTuple args,_) :: l,act) :: rest -> let constants , constrs, others = divide_rec rest in constants , add_to_division (make_construct_match true (List.length args) pathl) constrs TVoid (args @ l,act) , others | ((PRecord args,_) :: l,act) :: rest -> let constants , constrs, others = divide_rec rest in constants , add_to_division (make_record_match args pathl) constrs TVoid (List.map snd args @ l,act) , others | ((PStream ((SPattern p :: sl),k),pp) :: l,act) :: rest -> let constants , constrs, others = divide_rec rest in constants , always_add (make_token_match ((MToken (curpath,k)) :: pathl)) constrs (stream_pattern p :: (PStream (sl,k+1),pp) :: l, act) , others | ((PStream ((SMagicExpr ((PTuple _,_) as p,e) :: sl),k),pp) :: l,act) :: rest -> let constants , constrs, others = divide_rec rest in let bind = MExecute (mk (TConst (TIdent "@tmp")) t_void pp,false) in constants , always_add (make_token_match (junk curpath k (MExecute (Obj.magic e,false)) :: bind :: pathl)) constrs ((PConst (Ident "@tmp"),pp) :: stream_pattern p :: (PStream (sl,0),pp) :: l, act) , others | ((PStream ((SMagicExpr (p,e) :: sl),k),pp) :: l,act) :: rest -> let constants , constrs, others = divide_rec rest in constants , always_add (make_token_match (junk curpath k (MExecute (Obj.magic e,false)) :: pathl)) constrs (stream_pattern p :: (PStream (sl,0),pp) :: l, act) , others | ((PStream ([],k),pp) :: l,act) :: rest -> let constants , constrs, others = divide_rec rest in constants , always_add (make_constant_match pathl) constrs (l, junk curpath k act) , others | casel -> [] , [] , (casel,pathl) in divide_rec casel let rec conquer_divided_matching = function | [] -> [], Total, [] | (key, matchref) :: rest -> let l1, p1, u1 = conquer_matching !matchref in let l2, p2, u2 = conquer_divided_matching rest in (key , l1) :: l2 , total p1 p2 , u1 @ u2 and conquer_matching (m:matching) = match m with | [] , _ -> failure , Partial , [] | ([],action) :: rest , k -> if have_when action then let a , p , r = conquer_matching (rest,k) in handle action a , p , r else action , Total, rest | _ , [] -> assert false | (p :: _,_) :: _ , _ :: _ when start_by_a_variable p -> let vars , rest = split_matching m in let l1, p1, u1 = conquer_matching vars in let l2, p2, u2 = conquer_matching rest in if p1 = Total then l1 , Total, u1 @ lines_of_matching rest else handle l1 l2 , (if p2 = Total then Total else Dubious) , u1 @ u2 | _ , path :: _ -> match divide_matching m with | [] , [] , vars -> conquer_matching vars | consts , [] , vars -> let l1, _ , u1 = conquer_divided_matching consts in let l2, p2, u2 = conquer_matching vars in handle (cond path l1) l2 , p2 , u1 @ u2 | [] , constrs , vars -> let l1, p1, u1 = conquer_divided_matching constrs in let l2, p2, u2 = conquer_matching vars in if fully_matched (List.map fst constrs) && p1 = Total then switch path l1 , Total , u1 @ lines_of_matching vars else handle (switch path l1) l2 , partial p1 p2 , u1 @ u2 | _ -> assert false let make (cases : (pattern list * texpr option * texpr) list) p = let cases = List.concat (List.map (fun (pl,wcond,e) -> let e = exec e in let e = (match wcond with None -> e | Some e2 -> ewhen e2 e) in List.map (fun p -> [p] , e) pl ) cases) in let m = cases , [MRoot] in let lambda, partial, unused = conquer_matching m in (match unused with | [] -> () | ([] , _ ) :: _ -> error "Some pattern are never matched" p | ((_,p) :: _ , _) :: _ -> error "This pattern is never matched" p); partial <> Total , lambda neko-2-4-0/libs/ocaml/ml/mlneko.ml000066400000000000000000000357711464615675700167640ustar00rootroot00000000000000(* * NekoML Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Ast open Mltype type comparison = | Native | Structural type context = { module_name : string; mutable counter : int; mutable refvars : (string,unit) PMap.t; } let verbose = ref false let gen_label ctx = let c = ctx.counter in ctx.counter <- ctx.counter + 1; "l" ^ string_of_int c let gen_variable ctx = let c = ctx.counter in ctx.counter <- ctx.counter + 1; "v" ^ string_of_int c let module_name m = "@" ^ String.concat "_" m let builtin name = EConst (Builtin name) , Ast.null_pos let ident name = EConst (Ident name) , Ast.null_pos let int n = EConst (Int n) , Ast.null_pos let null = EConst Null , Ast.null_pos let core s p = EField ((EConst (Ident (module_name ["Core"])),p),s) , p let pos (p : Mlast.pos) = { pmin = p.Mlast.pmin; pmax = p.Mlast.pmax; pfile = p.Mlast.pfile; } let rec is_fun t = match t.texpr with | TNamed (_,_,t) | TLink t -> is_fun t | TPoly | TMono _ | TFun _ -> true | _ -> false let rec call ret f args p = match f with | EConst (Builtin _) , _ -> ECall (f,args) , p | _ -> match args with | a :: b :: c :: d :: x :: l -> let app = ECall ((EConst (Builtin "apply"),p),[f;a;b;c;d]) , p in call ret app (x :: l) p | _ -> if is_fun ret then ECall ((EConst (Builtin "apply"),p),f :: args) , p else ECall (f,args) , p let array args p = ECall ((EConst (Builtin "array"),p),args) , p let block e = match e with | EBlock _ , _ -> e | _ -> EBlock [e] , snd e let rec arity t = match t.texpr with | TAbstract -> 0 | TTuple tl -> List.length tl | TLink t -> arity t | _ -> 1 let comparison t = match tlinks true t with | TNamed (["int"],[],_) | TNamed (["char"],[],_) | TNamed (["float"],[],_) | TNamed (["string"],[],_) -> Native | _ -> Structural let rec gen_constant ctx c p = (match c with | TVoid -> EConst Null | TInt n when n < 0 -> EBinop ("-",int 0, int (-n)) | TInt n -> EConst (Int n) | TFloat s -> EConst (Float s) | TChar c -> EConst (Int (int_of_char c)) | TString s -> EConst (String s) | TIdent s -> if PMap.mem s ctx.refvars then EArray ((EConst (Ident s),null_pos),int 0) else EConst (Ident s) | TBool true -> EConst True | TBool false -> EConst False | TConstr "[]" | TModule (["Core"],TConstr "[]") -> fst (core "@empty" p) | TConstr "::" | TModule (["Core"],TConstr "::") -> fst (core "@cons" p) | TConstr s -> EConst (Ident s) | TModule ([],c) -> fst (gen_constant ctx c p) | TModule (m,c) -> EField ( (EConst (Ident (module_name m)),p) , (match c with TConstr x -> x | TIdent s -> s | _ -> assert false)) ) , p type match_context = { ctx : context; h : (match_op , string) Hashtbl.t; out : string; pos : Ast.pos; mutable next : string; mutable first : bool; } let no_label = "" let rec gen_match_rec mctx fail m = try ident (Hashtbl.find mctx.h m) with Not_found -> let p = mctx.pos in let ctx = mctx.ctx in let gen_rec = gen_match_rec mctx in match m with | MFailure -> let label = if mctx.first && mctx.next <> no_label then mctx.next else fail in if label = no_label then EBlock [] , p else call t_void (builtin "goto") [ident label] p | MHandle (m1,m2) -> let label = gen_label ctx in let m1 = gen_rec label m1 in let m2 = gen_rec fail m2 in EBlock [m1; ELabel label, p; m2] , p | MRoot -> assert false | MExecute (e,b) -> if not b then begin let ematch = EBinop ("==" , ident "@exc" , core "Stream_matching" p) , p in let reraise = EIf ( ematch , gen_rec fail MFailure, Some(ECall (builtin "throw", [ident "@exc"]) , p) ) , p in mctx.first <- false; match e.edecl with | TConst _ -> gen_expr ctx e | _ -> ETry (gen_expr ctx e, "@exc" , reraise ) , p end else begin mctx.first <- true; let out = call t_void (builtin "goto") [ident mctx.out] p in EBlock [gen_expr ctx e;out] , p end; | MConstants (m,[TIdent v,m1]) -> let m = gen_rec fail m in EBlock [ EVars [v, Some m] , p; gen_rec fail m1 ] , p | MConstants (m,cl) -> let e = gen_rec fail m in let v = gen_variable ctx in let exec = List.fold_left (fun acc (c,m) -> let test = EBinop ("==", ident v, gen_constant ctx c p) , p in let exec = gen_rec fail m in EIf (test, exec, Some acc) , p ) (gen_rec fail MFailure) (List.rev cl) in EBlock [ EVars [v, Some e] , p; exec ] , p | MRecordField (m,f) -> EField (gen_rec fail m, f) , p | MTuple (m,n) -> EArray (gen_rec fail m, int n) , p | MField (m,n) -> EArray (gen_rec fail m, int (n + 2)) , p | MNext (m1,m2) -> let old = mctx.next in let label = gen_label ctx in mctx.next <- label; let m1 = gen_rec fail m1 in mctx.next <- old; let m2 = gen_rec fail m2 in EBlock [m1; ELabel label, p; m2] , p | MSwitch (m,cl) -> let e = gen_rec fail m in let v = gen_variable ctx in let exec = List.fold_left (fun acc (c,m) -> let test = EBinop ("==", ident v, gen_constant ctx c p) , p in let exec = gen_rec fail m in EIf (test, exec, Some acc) , p ) (gen_rec fail MFailure) (List.rev cl) in EBlock [ EVars [v, Some (EArray (e,int 0),p)] , p; exec; ] , p | MBind (v,m1,m2) -> let e1 = gen_rec fail m1 in Hashtbl.add mctx.h m1 v; let e2 = gen_rec fail m2 in Hashtbl.remove mctx.h m1; EBlock [(EVars [v, Some e1] , p); e2] , p | MWhen (e,m) -> let e = gen_expr ctx e in let m = gen_rec fail m in let fail = gen_rec fail MFailure in EIf (e,m,Some fail) , p | MToken (m,n) -> call t_void (core "stream_token" p) [gen_rec fail m; int n] p | MJunk (m,n,m2) -> let m = gen_rec fail m in mctx.first <- false; EBlock [ call t_void (core "stream_junk" p) [m; int n] p; gen_rec fail m2 ] , p and gen_matching ctx v m p stream out = let mctx = { ctx = ctx; h = Hashtbl.create 0; pos = p; out = out; first = stream; next = no_label; } in let label = (if stream then gen_label ctx else no_label) in Hashtbl.add mctx.h MRoot v; let e = gen_match_rec mctx label m in if stream then begin let vpos = gen_variable ctx in let stream_pos = ECall (core "stream_pos" p, [ident v]) , p in let test = EBinop ("==", ident vpos , stream_pos) , p in let exc = ECall (builtin "throw",[EIf (test, core "Stream_matching" p , Some (core "Stream_error" p)) , p]) , p in EBlock [EVars [vpos , Some stream_pos] , p; e; ELabel label , p; exc] , p end else e and gen_match ctx e m stream p = let out = gen_label ctx in let v = gen_variable ctx in let m = gen_matching ctx v m p stream out in let m = ENext ((EVars [v,Some e],p),m) , p in EBlock [m; ELabel out , p] , p and gen_constructor ctx tname c t p = let field = ident c in let printer = EConst (Ident (tname ^ "__string")) , p in let val_type t = match arity t with | 0 -> let make = array [null;printer] p in ENext ((EBinop ("=" , field, make) ,p) , (EBinop("=" , (EArray (field,int 0),p) , field) , p)) , p | n -> let args = Array.to_list (Array.init n (fun n -> "p" ^ string_of_int n)) in let build = array (field :: printer :: List.map (fun a -> EConst (Ident a) , p) args) p in let func = EFunction (args, (EBlock [EReturn (Some build),p] , p)) , p in EBinop ("=" , field , func ) , p in let export = EBinop ("=", (EField (ident ctx.module_name,c),p) , field) , p in ENext (val_type t , export) , p and gen_type_printer ctx c t = let printer = mk (TConst (TModule (["Core"],TIdent "@print_union"))) t_void Mlast.null_pos in let e = mk (TCall (printer,[ mk (TConst (TString c)) t_string Mlast.null_pos; mk (TConst (TIdent "v")) t_void Mlast.null_pos ])) t_string Mlast.null_pos in e and gen_type ctx name t p = match t.texpr with | TAbstract | TMono _ | TPoly | TRecord _ | TTuple _ | TFun _ | TNamed (_,_,{ texpr = TNamed _ }) -> EBlock [] , p | TLink t -> gen_type ctx name t p | TNamed (name,_,t) -> let rec loop = function | [] -> assert false | [x] -> x | _ :: l -> loop l in gen_type ctx (loop name) t p | TUnion (_,constrs) -> let cmatch = gen_match ctx (ident "v") (MSwitch (MRoot,List.map (fun (c,t) -> let e = gen_type_printer ctx c t in TConstr c , MExecute (e,true) ) constrs)) false p in let printer = EFunction (["v"], cmatch) , p in let regs = List.map (fun (c,t) -> gen_constructor ctx name c t p) constrs in EBlock ((EVars [name ^ "__string",Some printer],p) :: regs) , p and gen_binop ctx op e1 e2 p = let compare op = let cmp = ECall (core "@compare" p,[gen_expr ctx e1; gen_expr ctx e2]) , p in EBinop (op , cmp , int 0) , p in let make op = EBinop (op,gen_expr ctx e1,gen_expr ctx e2) , p in let builtin op = ECall (builtin op,[gen_expr ctx e1; gen_expr ctx e2]) , p in match op with | "and" -> make "&" | "or" -> make "|" | "xor" -> make "^" | "==" | "!=" | ">" | "<" | ">=" | "<=" -> (match comparison e1.etype with | Structural -> compare op | Native -> make op) | "===" -> EBinop ("==", builtin "pcompare" , int 0) , p | "!==" -> EBinop ("!=" , builtin "pcompare" , int 0) , p | ":=" -> (match e1.edecl with | TField _ -> make "=" | TArray (a,i) -> ECall (core "@aset" p,[gen_expr ctx a; gen_expr ctx i; gen_expr ctx e2]) , p | _ -> EBinop ("=",(EArray (gen_expr ctx e1,int 0),pos e1.epos),gen_expr ctx e2) , p) | _ -> make op and gen_expr ctx e = let p = pos e.epos in match e.edecl with | TConst c -> gen_constant ctx c p | TBlock el -> EBlock (gen_block ctx el p) , p | TParenthesis e -> EParenthesis (gen_expr ctx e) , p | TCall ({ edecl = TConst (TIdent "neko") },[{ edecl = TConst (TString s) }]) | TCall ({ edecl = TConst (TModule ([],TIdent "neko")) },[{ edecl = TConst (TString s) }]) | TCall ({ edecl = TConst (TModule (["Core"],TIdent "neko")) },[{ edecl = TConst (TString s) }]) -> let ch = IO.input_string (String.concat "\"" (ExtString.String.nsplit s "'")) in let file = "neko@" ^ p.pfile in Parser.parse (Lexing.from_function (fun s p -> try IO.input ch s 0 p with IO.No_more_input -> 0)) file | TCall (f,el) -> let f = gen_expr ctx f in call e.etype f (List.map (gen_expr ctx) el) p | TField (e,s) -> EField (gen_expr ctx e, s) , p | TArray (e1,e2) -> let e1 = gen_expr ctx e1 in ECall (core "@aget" p,[e1;gen_expr ctx e2]) , p | TVar ([v],e) -> ctx.refvars <- PMap.remove v ctx.refvars; EVars [v , Some (gen_expr ctx e)] , p | TVar (vl,e) -> let n = ref (-1) in EVars (("@tmp" , Some (gen_expr ctx e)) :: List.map (fun v -> ctx.refvars <- PMap.remove v ctx.refvars; incr n; v , Some (EArray (ident "@tmp",int !n),p) ) vl) , p | TIf (e,e1,e2) -> let e = gen_expr ctx e in let e1 = gen_expr ctx e1 in EIf (e, e1, match e2 with None -> None | Some e2 -> Some (gen_expr ctx e2)) , p | TWhile (e1,e2) -> let e1 = gen_expr ctx e1 in let e2 = gen_expr ctx e2 in EWhile (e1, e2, NormalWhile) , p | TFunction (_,"_",params,e) -> EFunction (List.map fst params,block (gen_expr ctx e)) , p | TFunction (false,name,params,e) -> EVars [name , Some (EFunction (List.map fst params,block (gen_expr ctx e)) , p)] , p | TFunction _ -> EBlock [gen_functions ctx [e] p] , p | TBinop (op,e1,e2) -> gen_binop ctx op e1 e2 p | TTupleDecl tl -> array (List.map (gen_expr ctx) tl) p | TTypeDecl t -> gen_type ctx "" t p | TMut e -> gen_expr ctx (!e) | TRecordDecl fl -> EObject (("__string", core "@print_record" p) :: List.map (fun (s,e) -> s , gen_expr ctx e) fl) , p | TListDecl el -> (match el with | [] -> array [] p | x :: l -> let x = gen_expr ctx x in array [x; gen_expr ctx { e with edecl = TListDecl l }] p) | TUnop (op,e) -> (match op with | "-" -> EBinop ("-",int 0,gen_expr ctx e) , p | "*" -> EArray (gen_expr ctx e,int 0) , p | "!" -> call t_void (builtin "not") [gen_expr ctx e] p | "&" -> array [gen_expr ctx e] p | _ -> assert false) | TMatch (e,m,stream) -> gen_match ctx (gen_expr ctx e) m stream p | TTupleGet (e,n) -> EArray (gen_expr ctx e,int n) , p | TErrorDecl (e,t) -> let printer = gen_expr ctx (gen_type_printer ctx e t) in let printer = EFunction (["v"], (EBlock [printer],p)) , p in let printer = EVars [e ^ "__string",Some printer] , p in ENext (printer , gen_constructor ctx e e t p) , p | TTry (e,m) -> let out = gen_label ctx in let matching = gen_matching ctx "@exc" m p false out in let reraise = call t_void (builtin "throw") [ident "@exc"] p in let handle = EBlock [matching;reraise;ELabel out , p] , p in ETry (gen_expr ctx e,"@exc",handle) , p and gen_functions ctx fl p = let ell = ref (EVars (List.map (fun e -> match e.edecl with | TFunction (_,"_",params,e) -> "_" , Some (EFunction (List.map fst params,block (gen_expr ctx e)),p) | TFunction (_,name,_,_) -> ctx.refvars <- PMap.add name () ctx.refvars; name , Some (array [null] null_pos) | _ -> assert false ) fl) , null_pos) in List.iter (fun e -> let p = pos e.epos in match e.edecl with | TFunction (_,name,params,e) -> if name <> "_" then begin let e = gen_expr ctx e in let e = EFunction (List.map fst params,block e) , p in let e = EBinop ("=",(EArray (ident name,int 0),p),e) , p in let e = EBlock [e; EBinop ("=",ident name,(EArray (ident name,int 0),p)) , p] , p in ell := ENext (!ell, e) , p; ctx.refvars <- PMap.remove name ctx.refvars; end; | _ -> assert false ) fl; !ell and gen_block ctx el p = let old = ctx.refvars in let ell = ref [] in let rec loop fl = function | [] -> if fl <> [] then ell := gen_functions ctx (List.rev fl) p :: !ell | { edecl = TFunction (true,name,p,f) } as e :: l -> loop (e :: fl) l | { edecl = TMut r } :: l -> loop fl (!r :: l) | x :: l -> if fl <> [] then ell := gen_functions ctx (List.rev fl) p :: !ell; ell := gen_expr ctx x :: !ell; loop [] l in loop [] el; ctx.refvars <- old; List.rev !ell let generate e deps idents m = let m = module_name m in let ctx = { module_name = m; counter = 0; refvars = PMap.empty; } in if !verbose then print_endline ("Generating " ^ m ^ ".neko"); let init = EBinop ("=",ident m,builtin "exports"), null_pos in let deps = List.map (fun m -> let file = String.concat "/" m in let load = ECall ((EField (builtin "loader","loadmodule"),null_pos),[gen_constant ctx (TString file) null_pos;builtin "loader"]) , null_pos in EBinop ("=", ident (module_name m), load ) , null_pos ) deps in let exports = List.map (fun i -> EBinop ("=", (EField (builtin "exports",i),null_pos) , ident i) , null_pos ) idents in match gen_expr ctx e with | EBlock e , p -> EBlock (init :: deps @ e @ exports) , p | e -> EBlock (init :: deps @ e :: exports) , null_pos neko-2-4-0/libs/ocaml/ml/mlparser.ml000066400000000000000000000332541464615675700173160ustar00rootroot00000000000000(* * NekoML Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Mlast type error_msg = | Unexpected of token | Unclosed of string | Duplicate_default | Unknown_macro of string | Invalid_macro_parameters of string * int exception Error of error_msg * pos let error_msg = function | Unexpected t -> "Unexpected " ^ s_token t | Unclosed s -> "Unclosed " ^ s | Duplicate_default -> "Duplicate default declaration" | Unknown_macro m -> "Unknown macro " ^ m | Invalid_macro_parameters (m,n) -> "Invalid number of parameters for macro " ^ m ^ " : " ^ string_of_int n ^ " required" let error m p = raise (Error (m,p)) let priority = function | "+=" | "-=" | "*=" | "/=" | "|=" | "&=" | "^=" | ":=" -> -3 | "&&" | "||" -> -2 | "==" | "!=" | ">" | "<" | "<=" | ">=" | "===" | "!==" | "<>" | "=" -> -1 | "+" | "-" -> 0 | "*" | "/" -> 1 | "or" | "and" | "xor" -> 2 | "<<" | ">>" | "%" | ">>>" -> 3 | _ -> 4 let can_swap _op op = let p1 = priority _op in let p2 = priority op in if p1 < p2 then true else if p1 = p2 then op <> "::" else false let rec make_binop op e ((v,p2) as e2) = match v with | EBinop (_op,_e,_e2) when can_swap _op op -> let _e = make_binop op e _e in EBinop (_op,_e,_e2) , punion (pos _e) (pos _e2) | _ -> EBinop (op,e,e2) , punion (pos e) (pos e2) let rec make_unop op ((v,p2) as e) p1 = match v with | EBinop (bop,e,e2) -> EBinop (bop, make_unop op e p1 , e2) , (punion p1 p2) | _ -> EUnop (op,e), punion p1 p2 let rec make_list_pat p = function | [] -> PConstr ([],"[]",None) , p | x :: l -> let p = snd x in let params = PTuple [x;make_list_pat p l] , p in PConstr ([],"::",Some params) , p let rec make_list p = function | [] -> EConst (Constr "[]") , p | x :: l -> let p = snd x in ECall ((EConst (Constr "::") , p), [x;make_list p l]) , p let is_unop = function | "-" | "*" | "!" | "&" -> true | _ -> false let unclosed s p = error (Unclosed s) p let rec program = parser | [< e = expr; p = program >] -> e :: p | [< '(Semicolon,_); p = program >] -> p | [< '(Eof,_) >] -> [] and expr = parser | [< '(BraceOpen,p1); e = block1; s >] -> (match s with parser | [< '(BraceClose,p2); s >] -> e , punion p1 p2 | [< >] -> unclosed "{" p1) | [< '(Keyword Var,p1); '(Const (Ident name),_); t = type_opt; l = vars; e = expr; s >] -> EVar ((name,t) :: l,e) , punion p1 (pos e) | [< '(Keyword If,p1); cond = expr; '(Keyword Then,_); e = expr; s >] -> (match s with parser | [< '(Keyword Else,_); e2 = expr; s >] -> EIf (cond,e,Some e2) , punion p1 (pos e2) | [< >] -> EIf (cond,e,None) , punion p1 (pos e)) | [< '(Keyword Function,p1); r = function_rec; n = ident_opt; '(ParentOpen _,po); p = parameters_decl; t = type_opt; e = expr >] -> EFunction (r,n,p,e,t) , punion p1 (pos e) | [< '(Keyword Type,p1); pl = type_decl_parameters; '(Const (Ident tname),p2); d , p2 = type_declaration p2 >] -> ETypeDecl (pl,tname,d) , punion p1 p2 | [< '(Keyword Exception,p1); '(Const (Constr ename),p2); t = type_opt; s >] -> EErrorDecl (ename,t) , punion p1 p2 | [< '(Keyword Match,p1); e = expr; '(BraceOpen,po); pl = patterns_begin; s >] -> (match s with parser | [< '(BraceClose,pe); >] -> EMatch (e,pl), punion p1 pe | [< >] -> unclosed "{" po) | [< '(Keyword Try,p1); b = block; '(Keyword Catch,p2); '(BraceOpen,po); pl = patterns_begin; s >] -> (match s with parser | [< '(BraceClose,pe); >] -> ETry ((EBlock b,punion p1 p2),pl), punion p1 pe | [< >] -> unclosed "{" po) | [< '(Keyword While,p1); e = expr; '(BraceOpen,po); b = block; s >] -> (match s with parser | [< '(BraceClose,pe); s >] -> EWhile (e,(EBlock b,punion po pe)) , punion po pe | [< >] -> unclosed "{" po) | [< e = expr_short >] -> e and expr_short = parser | [< '(ParentOpen _,p1); pl = parameters; s >] -> (match s with parser | [< '(ParentClose,p2); s >] -> expr_next (ETupleDecl pl,punion p1 p2) s | [< >] -> unclosed "(" p1) | [< '(Binop op,p) when is_unop op; e = expr; s >] -> expr_next (make_unop op e p) s | [< '(Const (Constr n),p); e = expr_constr n p; s >] -> expr_next e s | [< '(Const c,p); s >] -> expr_next (EConst c,p) s | [< '(BracketOpen,p1); b = block; '(BracketClose,p2); s >] -> expr_next (make_list (punion p1 p2) b) s and expr_next e = parser | [< '(Binop ":",_); t , p = type_path; s >] -> expr_next (ETypeAnnot (e,t),punion (pos e) p) s | [< '(ParentOpen false,po); pl = parameters; s >] -> (match s with parser | [< '(ParentClose,p); s >] -> expr_next (ECall (e,pl),punion (pos e) p) s | [< >] -> unclosed "(" po) | [< '(Dot,_); s >] -> (match s with parser | [< '(Const (Ident name),p); s >] -> expr_next (EField (e,name),punion (pos e) p) s | [< '(BracketOpen,po); e2 = expr; s >] -> (match s with parser | [< '(BracketClose,p); s >] -> expr_next (EArray (e,e2),punion (pos e) p) s | [< >] -> unclosed "[" po)) | [< '(Binop op,_); e2 = expr; s >] -> make_binop op e e2 | [< ep = expr_short >] -> let rec loop ep = match ep with | EApply (e2,l) , p -> EApply (e,e2 :: l) , punion (pos e) p | EBinop (op,e1,e2) , p -> EBinop (op,loop e1,e2) , punion (pos e) p | _ -> EApply (e,[ep]) , punion (pos e) (pos ep) in loop ep | [< >] -> e and expr_constr n p = parser | [< '(Dot,_); e = expr_constr2 >] -> (match e with | EConst ((Ident _) as c) , p2 | EConst ((Constr _) as c) , p2 -> EConst (Module ([n],c)) , punion p p2 | EConst (Module (l,c)) , p2 -> EConst (Module (n :: l,c)) , punion p p2 | _ -> assert false); | [< >] -> EConst (Constr n), p and expr_constr2 = parser | [< '(Const (Ident n),p) >] -> EConst (Ident n) , p | [< '(Const (Constr n),p); e = expr_constr n p >] -> e and block1 = parser | [< '(Const (Ident name),p); s >] -> (match s with parser | [< '(Binop "=",_); e = expr; l = record_fields >] -> ERecordDecl ((name,e) :: l) | [< e = expr_next (EConst (Ident name),p); b = block >] -> EBlock (e :: b)) | [< b = block >] -> EBlock b and record_fields = parser | [< '(Const (Ident name),_); '(Binop "=",_); e = expr; l = record_fields >] -> (name,e) :: l | [< '(Semicolon,_); l = record_fields >] -> l | [< >] -> [] and vars = parser | [< '(Binop "=",_) >] -> [] | [< '(Comma,_); '(Const (Ident name),_); t = type_opt; l = vars >] -> (name,t) :: l and block = parser | [< e = expr; b = block >] -> e :: b | [< '(Semicolon,_); b = block >] -> b | [< >] -> [] and parameters_decl = parser | [< '(Const (Ident name),_); s >] -> parameters_decl_next (ANamed name) s | [< '(ParentOpen _,_); l = parameters_decl; s >] -> parameters_decl_next (ATuple l) s | [< '(ParentClose,_) >] -> [] and parameters_decl_next acc = parser | [< '(Comma,_); p = parameters_decl >] -> acc :: p | [< '(Binop ":",_); t , _ = type_path; s >] -> parameters_decl_next (ATyped (acc,t)) s | [< '(ParentClose,_) >] -> [acc] and type_opt = parser | [< '(Binop ":",_); t , _ = type_path; >] -> Some t | [< >] -> None and function_rec = parser | [< '(Const (Ident "rec"),_); >] -> true | [< >] -> false and ident_opt = parser | [< '(Const (Ident name),_); >] -> Some name | [< >] -> None and parameters = parser | [< e = expr; p = parameters_next >] -> e :: p | [< >] -> [] and parameters_next = parser | [< '(Comma,_); p = parameters >] -> p | [< >] -> [] and type_path = parser | [< '(Const (Ident tname),p); t = type_path_next (EType (None,[],tname)) p >] -> t | [< '(Const (Constr m),p); '(Dot,_); l = type_path_mod; '(Const (Ident tname),_); t = type_path_next (EType (None,m :: l,tname)) p >] -> t | [< '(Quote,_); '(Const (Ident a),p); t = type_path_next (EPoly a) p >] -> t | [< '(ParentOpen _,_); t , p = type_path; l , p = type_path_list_next p; '(ParentClose,_); s >] -> type_path_next (ETuple (t :: l)) p s and type_path_list p = parser | [< t , p = type_path; l , p = type_path_list_next p >] -> t :: l , p and type_path_list_next p = parser | [< '(Comma,_); t = type_path_list p >] -> t | [< >] -> [] , p and type_path_next t p = parser | [< '(Arrow,_); t2 , p = type_path >] -> EArrow(t,t2) , p | [< '(Const (Ident tname),p); t = type_path_next (EType (Some t,[],tname)) p >] -> t | [< '(Const (Constr m),p); '(Dot,_); l = type_path_mod; '(Const (Ident tname),_); t = type_path_next (EType (Some t,m :: l,tname)) p >] -> t | [< >] -> t , p and type_path_mod = parser | [< '(Const (Constr m),_); '(Dot,_); l = type_path_mod >] -> m :: l | [< >] -> [] and type_decl_parameters = parser | [< '(Quote,_); '(Const (Ident a),_); >] -> [a] | [< '(ParentOpen _,_); l = type_decl_plist; '(ParentClose,_); >] -> l | [< >] -> [] and type_decl_plist = parser | [< '(Quote,_); '(Const (Ident a),_); l = type_decl_plist_next >] -> a :: l and type_decl_plist_next = parser | [< '(Comma,_); l = type_decl_plist >] -> l | [< >] -> [] and type_declaration p = parser | [< '(BraceOpen,_); s >] -> (match s with parser | [< el , p = record_declaration false >] -> ERecord el , p | [< el , p = union_declaration >] -> EUnion el , p) | [< '(Binop "=",_); t , p = type_path >] -> EAlias t , p | [< >] -> EAbstract , p and record_declaration mut = parser | [< '(BraceClose,p) >] -> [] , p | [< '(Const (Ident "mutable"),_); l = record_declaration true; >] -> l | [< '(Semicolon,_); l = record_declaration false >] -> l | [< '(Const (Ident name),_); '(Binop ":",_); t , _ = type_path; l , p = record_declaration false >] -> (name,mut,t) :: l , p and union_declaration = parser | [< '(BraceClose,p) >] -> [] , p | [< '(Semicolon,_); l = union_declaration >] -> l | [< '(Const (Constr name),_); t = type_opt; l , p = union_declaration >] -> (name,t) :: l , p and patterns_begin = parser | [< '(Vertical,_); l = patterns >] -> l | [< l = patterns >] -> l and patterns = parser | [< p = pattern; pl = pattern_next; w = when_clause; '(Arrow,pa); b = block; l = patterns_begin >] -> let pat = (p :: pl,w,(match b with [e] -> e | _ -> EBlock b , pa) ) in pat :: l | [< >] -> [] and pattern_next = parser | [< '(Vertical,_); p = pattern; l = pattern_next >] -> p :: l | [< >] -> [] and pattern = parser | [< d , p = pattern_decl; s >] -> match s with parser | [< '(Const (Ident "as"),_); '(Const (Ident v),p2); s >] -> PAlias (v, (d,p)) , punion p p2 | [< '(Binop "::",_); d2 , p2 = pattern >] -> PConstr ([],"::",Some (PTuple [(d,p);(d2,p2)] , punion p p2)) , punion p p2 | [< t = type_opt >] -> match t with | None -> d , p | Some t -> PTyped ((d , p), t) , p and pattern_decl = parser | [< '(ParentOpen _,p1); pl = pattern_tuple; '(ParentClose,p2) >] -> PTuple pl , punion p1 p2 | [< '(BraceOpen,p1); '(Const (Ident name),_); '(Binop "=",_); p = pattern; pl = pattern_record; '(BraceClose,p2) >] -> PRecord ((name,p) :: pl) , punion p1 p2 | [< '(Const (Constr name),p1); l, name, p2 = pattern_mod_path name p1; p , p2 = pattern_opt p2 >] -> PConstr (l,name,p) , punion p1 p2 | [< '(Const (Ident i),p); >] -> PIdent i , p | [< '(Const c,p); >] -> PConst c , p | [< '(Binop "-",p1); '(Const (Int i),p2) >] -> PConst (Int (-i)) , punion p1 p2 | [< '(BracketOpen,p1); l = pattern_list; '(BracketClose,p2) >] -> make_list_pat (punion p1 p2) l | [< '(StreamOpen,p1); l = stream_list; '(StreamClose,p2) >] -> PStream (l,0) , punion p1 p2 and pattern_mod_path name p = parser | [< '(Dot,_); '(Const (Constr n),p); l, n, p = pattern_mod_path n p >] -> name :: l , n , p | [< >] -> [], name, p and stream_list = parser | [< '(Const (Ident v),p1); s >] -> (match s with parser | [< l = stream_ident_list; e = expr; s >] -> SExpr (v :: l,e) :: stream_next s | [< >] -> SPattern (PIdent v,p1) :: stream_next s) | [< p = pattern; l = stream_next >] -> SPattern p :: l | [< >] -> [] and stream_ident_list = parser | [< '(Comma,_); '(Const (Ident v),_); l = stream_ident_list >] -> v :: l | [< '(Binop "=",_) >] -> [] and stream_next = parser | [< '(Semicolon,_); l = stream_list >] -> l | [< >] -> [] and pattern_list = parser | [< p = pattern; l = pattern_list_next >] -> p :: l | [< >] -> [] and pattern_list_next = parser | [< '(Semicolon,_); l = pattern_list >] -> l | [< >] -> [] and pattern_tuple = parser | [< p = pattern; l = pattern_tuple_next >] -> p :: l | [< >] -> [] and pattern_tuple_next = parser | [< '(Comma,_); l = pattern_tuple >] -> l | [< >] -> [] and pattern_record = parser | [< '(Const (Ident name),_); '(Binop "=",_); p = pattern; l = pattern_record >] -> (name,p) :: l | [< '(Semicolon,_); l = pattern_record >] -> l | [< >] -> [] and pattern_opt p = parser | [< ( _ , pos as p) = pattern >] -> Some p , pos | [< >] -> None , p and when_clause = parser | [< '(Keyword When,_); e = expr >] -> Some e | [< >] -> None let parse code file = let old = Mllexer.save() in Mllexer.init file; let last = ref (Eof,null_pos) in let rec next_token x = let t, p = Mllexer.token code in match t with | Comment s | CommentLine s -> next_token x | _ -> last := (t , p); Some (t , p) in try let l = program (Stream.from next_token) in Mllexer.restore old; EBlock l, { pmin = 0; pmax = (pos !last).pmax; pfile = file } with | Stream.Error _ | Stream.Failure -> Mllexer.restore old; error (Unexpected (fst !last)) (pos !last) | e -> Mllexer.restore old; raise e neko-2-4-0/libs/ocaml/ml/mltype.ml000066400000000000000000000162451464615675700170040ustar00rootroot00000000000000(* * NekoML Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) type pos = Mlast.pos type mutflag = | Mutable | Immutable type type_expr = | TAbstract | TMono of int | TPoly | TRecord of (string * mutflag * t) list | TUnion of int * (string * t) list | TTuple of t list | TLink of t | TFun of t list * t | TNamed of string list * t list * t and t = { mutable tid : int; mutable texpr : type_expr; } type tconstant = | TVoid | TInt of int | TBool of bool | TFloat of string | TString of string | TChar of char | TIdent of string | TConstr of string | TModule of string list * tconstant and match_op = | MRoot | MFailure | MHandle of match_op * match_op | MExecute of texpr * bool | MConstants of match_op * (tconstant * match_op) list | MField of match_op * int | MTuple of match_op * int | MToken of match_op * int | MRecordField of match_op * string | MJunk of match_op * int * match_op | MSwitch of match_op * (tconstant * match_op) list | MBind of string * match_op * match_op | MWhen of texpr * match_op | MNext of match_op * match_op and texpr_decl = | TConst of tconstant | TBlock of texpr list | TParenthesis of texpr | TCall of texpr * texpr list | TField of texpr * string | TArray of texpr * texpr | TVar of string list * texpr | TIf of texpr * texpr * texpr option | TFunction of bool * string * (string * t) list * texpr | TBinop of string * texpr * texpr | TTupleDecl of texpr list | TTypeDecl of t | TMut of texpr ref | TRecordDecl of (string * texpr) list | TListDecl of texpr list | TUnop of string * texpr | TMatch of texpr * match_op * bool | TTry of texpr * match_op | TTupleGet of texpr * int | TErrorDecl of string * t | TWhile of texpr * texpr and texpr = { edecl : texpr_decl; etype : t; epos : pos; } type id_gen = int ref let pos e = e.epos let rec tlinks name t = match t.texpr with | TLink t -> tlinks name t | TNamed (_,_,t) when not name -> tlinks name t | _ -> t.texpr let etype name e = tlinks name e.etype let genid i = incr i; !i let generator() = ref 0 let mk e t p = { edecl = e; etype = t; epos = p; } let t_abstract = { tid = -1; texpr = TAbstract } let abstract s = { tid = -1; texpr = TNamed ([s],[], t_abstract); } let t_void = abstract "void" let t_int = abstract "int" let t_float = abstract "float" let t_char = abstract "char" let t_error = abstract "error" let t_bool = { tid = -1; texpr = TNamed (["bool"],[], { tid = -1; texpr = TUnion (2,[ ("true",{ tid = -1; texpr = TAbstract }); ("false",{ tid = -1; texpr = TAbstract }) ]); }); } let t_string = abstract "string" let t_mono g = { tid = -2; texpr = TMono (genid g); } let t_polymorph g = { tid = genid g; texpr = TPoly; } let t_poly g name = let param = t_mono g in { tid = genid g; texpr = TNamed ([name],[param], { tid = -1; texpr = TAbstract }); } , param let mk_fun g params ret = { tid = if List.exists (fun t -> t.tid <> -1) (ret :: params) then genid g else -1; texpr = TFun (params,ret); } let mk_tup g l = { tid = if List.exists (fun t -> t.tid <> -1) l then genid g else -1; texpr = TTuple l; } let mk_record g fl = { tid = if List.exists (fun (_,_,t) -> t.tid <> -1) fl then genid g else -1; texpr = TRecord fl; } let mk_union g fl = { tid = if List.exists (fun (_,t) -> t.tid <> -1) fl then genid g else -1; texpr = TUnion (List.length fl,fl); } type print_infos = { mutable pi_mcount : int; mutable pi_pcount : int; mutable pi_ml : (t * int) list; mutable pi_ph : (int , int) Hashtbl.t; } let s_context() = { pi_mcount = 0; pi_pcount = 0; pi_ml = []; pi_ph = Hashtbl.create 0; } let poly_id n = if n < 26 then String.make 1 (char_of_int (int_of_char 'a' + n)) else string_of_int (n - 25) let s_mutable = function | Mutable -> "mutable " | Immutable -> "" let rec s_type ?(ext=false) ?(h=s_context()) t = match t.texpr with | TAbstract -> "" | TMono _ -> Printf.sprintf "'_%s" (poly_id (try if t.tid <> -2 then assert false; List.assq t h.pi_ml with Not_found -> let k = h.pi_mcount in h.pi_mcount <- h.pi_mcount + 1; h.pi_ml <- (t,k) :: h.pi_ml; k)) | TPoly -> Printf.sprintf "'%s" (poly_id (try if t.tid = -1 then assert false; Hashtbl.find h.pi_ph t.tid with Not_found -> let k = h.pi_pcount in h.pi_pcount <- h.pi_pcount + 1; Hashtbl.add h.pi_ph t.tid k; k)) | TRecord fl -> Printf.sprintf "{ %s }" (String.concat "; " (List.map (fun (f,m,t) -> s_mutable m ^ f ^ " : " ^ s_type ~h t) fl)) | TUnion (_,fl) -> Printf.sprintf "{ %s }" (String.concat "; " (List.map (fun (f,t) -> f ^ " : " ^ s_type ~h t) fl)) | TTuple l -> Printf.sprintf "(%s)" (String.concat ", " (List.map (s_type ~h) l)) | TLink t -> s_type ~ext ~h t | TFun (tl,r) -> let l = String.concat " -> " (List.map (s_fun ~ext ~h) tl) ^ " -> " in l ^ s_type ~ext ~h r | TNamed (name,params,t) -> let s = (match params with | [] -> "" | [p] -> s_type ~h p ^ " " | l -> "(" ^ String.concat ", " (List.map (s_type ~h) l) ^ ") ") in let name = String.concat "." name in if ext then s ^ name ^ " = " ^ s_type ~h t else s ^ name and s_fun ~ext ~h t = match t.texpr with | TLink t -> s_fun ~ext ~h t | TFun _ -> "(" ^ s_type ~ext ~h t ^ ")" | _ -> s_type ~ext ~h t let rec duplicate g ?(h=Hashtbl.create 0) t = if t.tid < 0 then t else try Hashtbl.find h t.tid with Not_found -> let t2 = { tid = genid g; texpr = TAbstract; } in Hashtbl.add h t.tid t2; t2.texpr <- (match t.texpr with | TAbstract -> TAbstract | TMono _ -> assert false | TPoly -> t2.tid <- -2; TMono (genid g) | TRecord tl -> TRecord (List.map (fun (n,m,t) -> n , m, duplicate g ~h t) tl) | TUnion (n,tl) -> TUnion (n,List.map (fun (n,t) -> n , duplicate g ~h t) tl) | TTuple tl -> TTuple (List.map (duplicate g ~h) tl) | TLink t -> TLink (duplicate g ~h t) | TFun (tl,t) -> TFun (List.map (duplicate g ~h) tl, duplicate g ~h t) | TNamed (n,p,t) -> TNamed (n,List.map (duplicate g ~h) p,duplicate g ~h t)); t2 let rec polymorphize g mink t = if t.tid = -1 then () else match t.texpr with | TAbstract -> () | TMono k -> if k > mink then begin t.texpr <- TPoly; t.tid <- genid g end; | TPoly -> () | TRecord fl -> List.iter (fun (_,_,t) -> polymorphize g mink t) fl | TUnion (_,fl) -> List.iter (fun (_,t) -> polymorphize g mink t) fl | TTuple tl -> List.iter (polymorphize g mink) tl | TLink t -> polymorphize g mink t | TFun (tl,t) -> List.iter (polymorphize g mink) tl; polymorphize g mink t | TNamed (_,tl,t) -> List.iter (polymorphize g mink) tl neko-2-4-0/libs/ocaml/ml/mltyper.ml000066400000000000000000000747411464615675700171730ustar00rootroot00000000000000(* * NekoML Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Mlast open Mltype type module_context = { path : string list; types : (string,t) Hashtbl.t; constrs : (string, t * t) Hashtbl.t; records : (string,t * t * mutflag) Hashtbl.t; deps : (string list, module_context) Hashtbl.t; mutable expr : texpr option; mutable idents : (string,t) PMap.t; } type context = { gen : id_gen; mutable mink : int; mutable functions : (bool * string * texpr ref * t * (string * t) list * expr * t * pos) list; mutable opens : module_context list; mutable curfunction : string; tmptypes : (string, t * t list * (string,t) Hashtbl.t) Hashtbl.t; current : module_context; modules : (string list, module_context) Hashtbl.t; classpath : string list; } type error_msg = | Cannot_unify of t * t | Have_no_field of t * string | Stack of error_msg * error_msg | Unknown_field of string | Module_not_loaded of module_context | Custom of string exception Error of error_msg * pos module SSet = Set.Make(String) let rec error_msg ?(h=s_context()) = function | Cannot_unify (t1,t2) -> "Cannot unify " ^ s_type ~h t1 ^ " and " ^ s_type ~h t2 | Have_no_field (t,f) -> s_type ~h t ^ " have no field " ^ f | Stack (m1,m2) -> error_msg ~h m1 ^ "\n " ^ error_msg ~h m2 | Unknown_field s -> "Unknown field " ^ s | Module_not_loaded m -> "Module " ^ String.concat "." m.path ^ " require an interface" | Custom s -> s let error m p = raise (Error (m,p)) let verbose = ref false let load_module_ref = ref (fun _ _ -> assert false) let add_local ctx v t = if v <> "_" then ctx.current.idents <- PMap.add v t ctx.current.idents let save_locals ctx = ctx.current.idents let restore_locals ctx l = ctx.current.idents <- l let get_module ctx path p = match path with | [] -> ctx.current | _ -> let m = (try Hashtbl.find ctx.modules path with Not_found -> !load_module_ref ctx path p) in if m != ctx.current then begin if m.expr = None then error (Module_not_loaded m) p; Hashtbl.replace ctx.current.deps m.path m; end; m let get_type ctx path name p = let rec loop = function | [] -> error (Custom ("Unknown type " ^ s_path path name)) p | m :: l -> try Hashtbl.find m.types name with Not_found -> loop l in match path with | [] -> loop (ctx.current :: ctx.opens) | _ -> loop [get_module ctx path p] let get_constr ctx path name p = let rec loop = function | [] -> error (Custom ("Unknown constructor " ^ s_path path name)) p | m :: l -> try let t1, t2 = Hashtbl.find m.constrs name in (if m == ctx.current then [] else m.path) , t1, t2 with Not_found -> loop l in match path with | [] -> loop (ctx.current :: ctx.opens) | _ -> loop [get_module ctx path p] let get_ident ctx path name p = let rec loop = function | [] -> error (Custom ("Unknown identifier " ^ s_path path name)) p | m :: l -> try (if m == ctx.current then [] else m.path) , PMap.find name m.idents with Not_found -> loop l in match path with | [] -> loop (ctx.current :: ctx.opens) | _ -> loop [get_module ctx path p] let get_record ctx f p = let rec loop = function | [] -> error (Unknown_field f) p | m :: l -> try Hashtbl.find m.records f with Not_found -> loop l in let rt , ft , mut = loop (ctx.current :: ctx.opens) in let h = Hashtbl.create 0 in duplicate ctx.gen ~h rt, duplicate ctx.gen ~h ft, mut let rec is_tuple t = match t.texpr with | TLink t -> is_tuple t | TTuple _ -> true | TNamed(_,_,t) -> is_tuple t | _ -> false let rec is_recursive t1 t2 = if t1 == t2 then true else match t2.texpr with | TAbstract | TMono _ | TPoly -> false | TRecord _ | TUnion _ -> assert false | TTuple tl -> List.exists (is_recursive t1) tl | TLink t -> is_recursive t1 t | TFun (tl,t) -> List.exists (is_recursive t1) tl || is_recursive t1 t | TNamed (_,p,t) -> List.exists (is_recursive t1) p let link ctx t1 t2 p = if is_recursive t1 t2 then error (Cannot_unify (t1,t2)) p; t1.texpr <- TLink t2; if t1.tid < 0 then begin if t2.tid = -1 then t1.tid <- -1 else t1.tid <- genid ctx.gen; end else if t2.tid = -1 then t1.tid <- -1 let unify_stack t1 t2 = function | Error (Cannot_unify _ as e , p) -> error (Stack (e , Cannot_unify (t1,t2))) p | e -> raise e let is_alias = function | TAbstract | TRecord _ | TUnion _ -> false | TMono _ | TPoly | TTuple _ | TLink _ | TFun _ | TNamed _ -> true let rec propagate k t = match t.texpr with | TAbstract | TPoly -> () | TUnion _ | TRecord _ -> assert false | TMono k2 -> if k < k2 then t.texpr <- TMono k | TTuple tl -> List.iter (propagate k) tl | TLink t -> propagate k t | TFun (tl,t) -> propagate k t; List.iter (propagate k) tl | TNamed (_,tl,_) -> List.iter (propagate k) tl let rec unify ctx t1 t2 p = if t1 == t2 then () else match t1.texpr , t2.texpr with | TLink t , _ -> unify ctx t t2 p | _ , TLink t -> unify ctx t1 t p | TMono k , t -> link ctx t1 t2 p; propagate k t2 | t , TMono k -> link ctx t2 t1 p; propagate k t1 | TPoly , t -> link ctx t1 t2 p | t , TPoly -> link ctx t2 t1 p | TNamed (n1,p1,_) , TNamed (n2,p2,_) when n1 = n2 -> (try List.iter2 (fun p1 p2 -> unify ctx p1 p2 p) p1 p2 with e -> unify_stack t1 t2 e) | TNamed (_,_,t1) , _ when is_alias t1.texpr -> (try unify ctx t1 t2 p with e -> unify_stack t1 t2 e) | _ , TNamed (_,_,t2) when is_alias t2.texpr -> (try unify ctx t1 t2 p with e -> unify_stack t1 t2 e) | TFun (tl1,r1) , TFun (tl2,r2) when List.length tl1 = List.length tl2 -> (try List.iter2 (fun t1 t2 -> unify ctx t1 t2 p) tl1 tl2; unify ctx r1 r2 p; with e -> unify_stack t1 t2 e) | TTuple tl1 , TTuple tl2 when List.length tl1 = List.length tl2 -> (try List.iter2 (fun t1 t2 -> unify ctx t1 t2 p) tl1 tl2 with e -> unify_stack t1 t2 e) | _ , _ -> error (Cannot_unify (t1,t2)) p let rec type_type ?(allow=true) ?(h=Hashtbl.create 0) ctx t p = match t with | ETuple [] -> assert false | ETuple [t] -> type_type ~allow ~h ctx t p | ETuple el -> mk_tup ctx.gen (List.map (fun t -> type_type ~allow ~h ctx t p) el) | EPoly s -> (try Hashtbl.find h s with Not_found -> if not allow then error (Custom ("Unbound type variable '" ^ s)) p; let t = t_mono ctx.gen in Hashtbl.add h s t; t) | EType (param,path,name) -> let param = (match param with None -> None | Some t -> Some (type_type ~allow ~h ctx t p)) in let t = get_type ctx path name p in (match t.texpr with | TNamed (_,params,t2) -> let tl = (match params, param with | [] , None -> [] | [x] , Some t -> [t] | l , Some { texpr = TTuple tl } when List.length tl = List.length l -> tl | _ , _ -> error (Custom ("Invalid number of type parameters for " ^ s_path path name)) p ) in let h = Hashtbl.create 0 in let t = duplicate ctx.gen ~h t in let params = List.map (duplicate ctx.gen ~h) params in List.iter2 (fun pa t -> unify ctx pa t p) params tl; t | _ -> assert false) | EArrow _ -> let rec loop params t = match t with | EArrow (ta,tb) -> let ta = type_type ~allow ~h ctx ta p in loop (ta :: params) tb | _ -> let t = type_type ~allow ~h ctx t p in mk_fun ctx.gen (List.rev params) t in loop [] t let rec type_constant ctx ?(path=[]) c p = match c with | Int i -> mk (TConst (TInt i)) t_int p | Float s -> mk (TConst (TFloat s)) t_float p | String s -> mk (TConst (TString s)) t_string p | Bool b -> mk (TConst (TBool b)) t_bool p | Char c -> mk (TConst (TChar c)) t_char p | Ident s -> let path , t = get_ident ctx path s p in let t = duplicate ctx.gen t in mk (TConst (TModule (path,TIdent s))) t p | Constr s -> let path , ut , t = get_constr ctx path s p in let t = duplicate ctx.gen (match t.texpr with | TAbstract -> ut | TTuple tl -> mk_fun ctx.gen tl ut | _ -> mk_fun ctx.gen [t] ut) in mk (TConst (TModule (path,TConstr s))) t p | Module (path,c) -> type_constant ctx ~path c p type addable = NInt | NFloat | NString | NNan let addable str e = match etype true e with | TNamed (["int"],_,_) -> NInt | TNamed (["float"],_,_) -> NFloat | TNamed (["string"],_,_) when str -> NString | _ -> NNan let type_binop ctx op e1 e2 p = let emk t = mk (TBinop (op,e1,e2)) t p in match op with | "%" | "+" | "-" | "/" | "*" -> let str = (op = "+") in (match addable str e1, addable str e2 with | NInt , NInt -> emk t_int | NFloat , NFloat | NInt , NFloat | NFloat , NInt -> emk t_float | NInt , NString | NFloat , NString | NString , NInt | NString , NFloat | NString , NString -> emk t_string | NInt , NNan | NFloat , NNan | NString , NNan -> unify ctx e2.etype e1.etype (pos e2); emk e1.etype | NNan , NInt | NNan , NFloat | NNan , NString -> unify ctx e1.etype e2.etype (pos e1); emk e2.etype | NNan , NNan -> unify ctx e1.etype t_int (pos e1); unify ctx e2.etype t_int (pos e2); emk t_int) | ">>" | ">>>" | "<<" | "and" | "or" | "xor" -> unify ctx e1.etype t_int (pos e1); unify ctx e2.etype t_int (pos e2); emk t_int | "&&" | "||" -> unify ctx e1.etype t_bool (pos e1); unify ctx e2.etype t_bool (pos e2); emk t_bool | "<" | "<=" | ">" | ">=" | "==" | "!=" | "===" | "!==" -> unify ctx e2.etype e1.etype (pos e2); emk t_bool | ":=" -> (match e1.edecl with | TArray _ -> unify ctx e2.etype e1.etype (pos e2); emk t_void | TField (e,f) -> (match tlinks false e.etype with | TRecord fl -> let _ , mut , _ = (try List.find (fun (f2,_,_) -> f2 = f) fl with Not_found -> assert false) in if mut = Immutable then error (Custom ("Field " ^ f ^ " is not mutable")) (pos e1); unify ctx e2.etype e1.etype (pos e2); emk t_void | _ -> assert false); | _ -> let t , pt = t_poly ctx.gen "ref" in unify ctx e2.etype pt (pos e2); unify ctx e1.etype t (pos e1); emk t_void) | "::" -> let t , pt = t_poly ctx.gen "list" in unify ctx e1.etype pt (pos e1); unify ctx e2.etype t (pos e2); let c = mk (TConst (TConstr "::")) (t_mono ctx.gen) p in mk (TCall (c,[e1;e2])) t p | _ -> error (Custom ("Invalid operation " ^ op)) p let type_unop ctx op e p = let emk t = mk (TUnop (op,e)) t p in match op with | "&" -> let p , pt = t_poly ctx.gen "ref" in unify ctx e.etype pt (pos e); emk p | "*" -> let p , pt = t_poly ctx.gen "ref" in unify ctx e.etype p (pos e); emk pt | "!" -> unify ctx e.etype t_bool (pos e); emk t_bool | "-" -> (match addable false e with | NInt -> emk t_int | NFloat -> emk t_float | _ -> unify ctx e.etype t_int (pos e); emk t_int) | _ -> assert false let rec type_arg ctx h binds p = function | ATyped (a,t) -> let n , ta = type_arg ctx h binds p a in unify ctx ta (type_type ~h ctx t p) p; n , ta | ANamed s -> s , t_mono ctx.gen | ATuple al -> let aname = "@t" ^ string_of_int (genid ctx.gen) in let nl , tl = List.split (List.map (type_arg ctx h binds p) al) in let k = ref 0 in List.iter (fun n -> if n <> "_" then binds := (aname,!k,n) :: !binds; incr k; ) nl; aname , mk_tup ctx.gen tl let register_function ctx isrec name pl e rt p = if ctx.functions = [] then ctx.mink <- !(ctx.gen); let pl = (match pl with [] -> [ATyped (ANamed "_",EType (None,[],"void"))] | _ -> pl) in let expr = ref (mk (TConst TVoid) t_void p) in let h = Hashtbl.create 0 in let binds = ref [] in let el = List.map (type_arg ctx h binds p) pl in let name = (match name with None -> "_" | Some n -> n) in let e = (match List.rev !binds with | [] -> e | l -> EBlock (List.fold_left (fun acc (v,n,v2) -> (EVar ([v2,None], (ETupleGet ((EConst (Ident v),p),n),p)) , p) :: acc ) [e] l) , p ) in let rt = (match rt with | None -> t_mono ctx.gen | Some rt -> type_type ~h ctx rt p ) in let ft = mk_fun ctx.gen (List.map snd el) rt in ctx.functions <- (isrec,name,expr,ft,el,e,rt,p) :: ctx.functions; if isrec then add_local ctx name ft; mk (TMut expr) (if name = "_" then ft else t_void) p let type_format ctx s p = let types = ref [] in let percent = ref false in for i = 0 to String.length s - 1 do let c = String.get s i in if !percent then begin percent := false; match c with | '%' -> () | 'x' | 'X' | 'd' -> types := t_int :: !types | 'f' -> types := t_float :: !types | 's' -> types := t_string :: !types | 'b' -> types := t_bool :: !types | 'c' -> types := t_char :: !types | '0'..'9' | '.' -> percent := true | _ -> error (Custom "Invalid % sequence") p end else match c with | '%' -> percent := true | _ -> () done; if !percent then error (Custom "Invalid % sequence") p; match !types with | [] -> t_void | [x] -> x | l -> mk_tup ctx.gen (List.rev l) let rec type_functions ctx = let l = ctx.functions in if l <> [] then let mink = ctx.mink in ctx.functions <- []; let l = List.map (fun (isrec,name,expr,ft,el,e,rt,p) -> let locals = save_locals ctx in let func = ctx.curfunction in if name <> "_" then begin let fname = s_path ctx.current.path name in if !verbose then prerr_endline ("Typing " ^ fname); ctx.curfunction <- fname; end; List.iter (fun (p,pt) -> add_local ctx p pt ) el; let e = type_expr ctx e in restore_locals ctx locals; ctx.curfunction <- func; let ft2 = mk_fun ctx.gen (List.map snd el) e.etype in unify ctx ft ft2 p; expr := mk (TFunction (isrec,name,el,e)) ft2 p; if not isrec then add_local ctx name ft; ft2 ) (List.rev l) in List.iter (polymorphize ctx.gen mink) l and type_expr ctx (e,p) = match e with | EConst c -> type_constant ctx c p | EBlock [] -> mk (TConst TVoid) t_void p | EBlock (e :: l) -> let locals = save_locals ctx in let e = type_block ctx e in let el , t = List.fold_left (fun (l,t) e -> unify ctx t t_void (List.hd l).epos; let e = type_block ctx e in e :: l , e.etype ) ([e] , e.etype) l in type_functions ctx; restore_locals ctx locals; mk (TBlock (List.rev el)) t p | EApply (e,el) -> type_expr ctx (ECall (e,el),p) | ECall ((EConst (Ident "open"),_),[EConst (Module (m,Constr modname)),p]) -> ctx.opens <- get_module ctx (m @ [modname]) p :: ctx.opens; mk (TConst TVoid) t_void p | ECall ((EConst (Ident "open"),_),[EConst (Constr modname),p]) -> ctx.opens <- get_module ctx [modname] p :: ctx.opens; mk (TConst TVoid) t_void p | ECall ((EConst (Ident "assert"),_) as a,[]) -> let line = Mllexer.get_error_line p in type_expr ctx (ECall (a,[EConst (String p.pfile),p;EConst (Int line),p]),p) | ECall ((EConst (Ident "invalid_arg"),_) as a,[]) -> type_expr ctx (ECall (a,[EConst (String ctx.curfunction),p]),p) | ECall ((EConst (Constr "TYPE"),_),[e]) -> let e = type_expr ctx e in prerr_endline ("type : " ^ s_type e.etype); mk (TParenthesis e) t_void p | ECall (e,el) -> let e = type_expr ctx e in let el = (match el with [] -> [ETupleDecl [],p] | _ -> el) in let el = List.map (type_expr ctx) el in (match etype false e with | TFun (args,r) -> let rec loop acc expr l tl r = match l , tl with | e :: l , t :: tl -> (match tlinks true t with | TNamed (["format"],[param],_) -> (match e.edecl with | TConst (TString s) -> let tfmt = type_format ctx s e.epos in unify ctx param tfmt e.epos; | _ -> (match tlinks true e.etype with | TNamed (["format"],[param2],_) -> unify ctx param2 param e.epos | _ -> error (Custom "Constant string required for format") e.epos)) | _ -> unify ctx e.etype t (pos e)); loop (e :: acc) expr l tl r | [] , [] -> mk (TCall (expr,List.rev acc)) r p | [] , tl -> mk (TCall (expr,List.rev acc)) (mk_fun ctx.gen tl r) p | el , [] -> match tlinks false r with | TFun (args,r2) -> loop [] (mk (TCall (expr,List.rev acc)) r p) el args r2 | _ -> error (Custom "Too many arguments") p in loop [] e el args r | _ -> let r = t_mono ctx.gen in let f = mk_fun ctx.gen (List.map (fun e -> e.etype) el) r in unify ctx e.etype f p; mk (TCall (e,el)) r p ); | EField (e,s) -> let e = type_expr ctx e in let t = (match etype false e with | TRecord fl -> (try let _ , _ , t = List.find (fun (s2,_,_) -> s = s2) fl in t with Not_found -> error (Have_no_field (e.etype,s)) p) | _ -> let r , t , _ = get_record ctx s p in unify ctx e.etype r (pos e); t ) in mk (TField (e,s)) t p | EArray (e,ei) -> let e = type_expr ctx e in let ei = type_expr ctx ei in unify ctx ei.etype t_int (pos ei); let t , pt = t_poly ctx.gen "array" in unify ctx e.etype t (pos e); mk (TArray (e,ei)) pt p | EVar _ -> error (Custom "Variable declaration not allowed outside a block") p | EIf (e,e1,None) -> let e = type_expr ctx e in unify ctx e.etype t_bool (pos e); let e1 = type_expr ctx e1 in unify ctx e1.etype t_void (pos e1); mk (TIf (e,e1,None)) t_void p | EIf (e,e1,Some e2) -> let e = type_expr ctx e in unify ctx e.etype t_bool (pos e); let e1 = type_expr ctx e1 in let e2 = type_expr ctx e2 in unify ctx e2.etype e1.etype (pos e2); mk (TIf (e,e1,Some e2)) e1.etype p | EWhile (e1,e2) -> let e1 = type_expr ctx e1 in unify ctx e1.etype t_bool (pos e1); let e2 = type_expr ctx e2 in unify ctx e2.etype t_void (pos e2); mk (TWhile (e1,e2)) t_void p | EFunction (isrec,name,pl,e,rt) -> let r = register_function ctx isrec name pl e rt p in type_functions ctx; r | EBinop (op,e1,e2) -> type_binop ctx op (type_expr ctx e1) (type_expr ctx e2) p | ETypeAnnot (e,t) -> let e = type_expr ctx e in let t = type_type ctx t p in unify ctx e.etype t (pos e); mk e.edecl t p | ETupleDecl [] -> mk (TConst TVoid) t_void p | ETupleDecl [e] -> let e = type_expr ctx e in mk (TParenthesis e) e.etype (pos e) | ETupleDecl el -> let el = List.map (type_expr ctx) el in mk (TTupleDecl el) (mk_tup ctx.gen (List.map (fun e -> e.etype) el)) p | ETypeDecl (params,tname,decl) -> let fullname = (match ctx.current.path with ["Core"] -> [tname] | p -> p @ [tname]) in let t , tl , h = try let t , tl , h = Hashtbl.find ctx.tmptypes tname in if decl <> EAbstract then Hashtbl.remove ctx.tmptypes tname; if List.length tl <> List.length params then error (Custom ("Invalid number of parameters for type " ^ tname)) p; t , tl , h with Not_found -> if Hashtbl.mem ctx.current.types tname then error (Custom ("Invalid type redefinition of type " ^ tname)) p; let h = Hashtbl.create 0 in let tl = List.map (fun p -> let t = t_mono ctx.gen in Hashtbl.add h p t; t ) params in let t = { tid = -1; texpr = TNamed (fullname,tl,t_abstract); } in Hashtbl.add ctx.current.types tname t; if decl = EAbstract then Hashtbl.add ctx.tmptypes tname (t,tl,h); t , tl , h in let t2 = (match decl with | EAbstract -> t_abstract | EAlias t -> type_type ~allow:false ~h ctx t p | ERecord fields -> let fields = List.map (fun (f,m,ft) -> let ft = type_type ~allow:false ~h ctx ft p in let m = (if m then Mutable else Immutable) in Hashtbl.add ctx.current.records f (t,ft,m); f , m , ft ) fields in mk_record ctx.gen fields | EUnion constr -> let constr = List.map (fun (c,ft) -> let ft = (match ft with | None -> t_abstract | Some ft -> type_type ~allow:false ~h ctx ft p ) in Hashtbl.add ctx.current.constrs c (t,ft); c , ft ) constr in mk_union ctx.gen constr ) in t.tid <- if t2.tid = -1 && params = [] then -1 else genid ctx.gen; t.texpr <- TNamed (fullname,tl,t2); polymorphize ctx.gen 0 t; mk (TTypeDecl t) t_void p | ERecordDecl fl -> let s , _ = (try List.hd fl with _ -> assert false) in let r , _ , _ = get_record ctx s p in let fll = (match tlinks false r with | TRecord fl -> fl | _ -> assert false ) in let fl2 = ref fll in let rec loop f = function | [] -> if List.exists (fun (f2,_,_) -> f = f2) fll then error (Custom ("Duplicate declaration for field " ^ f)) p else error (Have_no_field (r,f)) p | (f2,_,ft) :: l when f = f2 -> ft , l | x :: l -> let t , l = loop f l in t , x :: l in let el = List.map (fun (f,e) -> let ft , fl2b = loop f !fl2 in fl2 := fl2b; let e = type_expr ctx e in unify ctx e.etype ft (pos e); (f , e) ) fl in List.iter (fun (f,_,_) -> error (Custom ("Missing field " ^ f ^ " in record declaration")) p; ) !fl2; mk (TRecordDecl el) r p | EErrorDecl (name,t) -> let t = (match t with None -> t_abstract | Some t -> type_type ~allow:false ctx t p) in Hashtbl.add ctx.current.constrs name (t_error,t); mk (TErrorDecl (name,t)) t_void p | EUnop (op,e) -> type_unop ctx op (type_expr ctx e) p | EMatch (e,cl) -> let e = type_expr ctx e in let is_stream = List.for_all (fun (l,_,_) -> List.for_all (fun (p,_) -> match p with PStream _ -> true | _ -> false) l) cl in let partial , m , t = type_match ctx e.etype cl p in if not is_stream && partial then error (Custom "This matching is not complete") p; mk (TMatch (e,m,is_stream)) t p | ETry (e,cl) -> let e = type_expr ctx e in let _ , m , t = type_match ctx t_error cl p in unify ctx t e.etype p; mk (TTry (e,m)) t p | ETupleGet (e,n) -> let e = type_expr ctx e in let try_unify et = let t = Array.init (n + 1) (fun _ -> t_mono ctx.gen) in unify ctx et (mk_tup ctx.gen (Array.to_list t)) p; t.(n) in let rec loop et = match et.texpr with | TLink et -> loop et | TTuple l -> (try List.nth l n with _ -> try_unify et) | _ -> try_unify et in mk (TTupleGet (e,n)) (loop e.etype) p and type_block ctx ((e,p) as x) = match e with | EVar (vl,e) -> type_functions ctx; let e = type_expr ctx e in let make v t = let t = (match t with | None -> t_mono ctx.gen | Some t -> type_type ctx t p ) in add_local ctx v t; t in let t = (match vl with | [] -> assert false | [v,t] -> make v t | _ -> mk_tup ctx.gen (List.map (fun (v,t) -> make v t) vl) ) in unify ctx t e.etype (pos e); mk (TVar (List.map fst vl,e)) t_void p | EFunction (true,name,pl,e,rt) -> register_function ctx true name pl e rt p | _ -> type_functions ctx; type_expr ctx x and type_pattern (ctx:context) h ?(h2 = Hashtbl.create 0) set add (pat,p) = let pvar add s = if SSet.mem s !set then error (Custom "This variable is several time in the pattern") p; set := SSet.add s !set; try Hashtbl.find h s with Not_found -> let t = t_mono ctx.gen in Hashtbl.add h s t; if add then add_local ctx s t; t in let pt , pat = (match pat with | PConst c -> (match c with | Int n -> t_int | Float s -> t_float | String s -> t_string | Char c -> t_char | Bool b -> t_bool | Ident _ | Constr _ | Module _ -> assert false) , pat | PTuple [p] -> let pt , pat = type_pattern ctx h ~h2 set add p in pt , fst pat | PTuple pl -> let pl , patl = List.split (List.map (type_pattern ctx h ~h2 set add) pl) in mk_tup ctx.gen pl , PTuple patl | PRecord fl -> let s = (try fst (List.hd fl) with _ -> assert false) in let r , _ , _ = get_record ctx s p in let fl = (match tlinks false r with | TRecord rl -> List.map (fun (f,pat) -> let pt , pat = type_pattern ctx h ~h2 set add pat in let t = (try let _ , _ , t = List.find (fun (f2,_,_) -> f = f2 ) rl in t with Not_found -> error (Have_no_field (r,f)) p ) in unify ctx pt t (snd pat); f , pat ) fl | _ -> assert false ) in r , PRecord fl | PIdent s -> (if s = "_" then t_mono ctx.gen else pvar add s) , pat | PConstr (path,s,param) -> let tparam , param = (match param with | None -> None , None | Some ((_,p) as param) -> let t , pat = type_pattern ctx h ~h2 set add param in Some (p,t) , Some pat ) in let path , ut , t = get_constr ctx path s p in (match t.texpr , tparam with | TAbstract , None -> duplicate ctx.gen ut , PConstr (path,s,param) | TAbstract , Some _ -> error (Custom "Constructor does not take parameters") p | _ , None -> error (Custom "Constructor require parameters") p | _ , Some (p,pt) -> let h = Hashtbl.create 0 in let ut = duplicate ctx.gen ~h ut in let t = duplicate ctx.gen ~h t in let param , pt = (match param with | Some (PTuple l,p) when not (is_tuple t) -> Some (PTuple [(PTuple l,p)],p) , mk_fun ctx.gen [pt] ut | Some (PIdent "_",p) -> param , pt | _ -> param , (match pt.texpr with TTuple l -> mk_fun ctx.gen l ut | _ -> mk_fun ctx.gen [pt] ut) ) in let t = (match t.texpr with TTuple l -> mk_fun ctx.gen l ut | _ -> mk_fun ctx.gen [t] ut) in unify ctx t pt p; ut , PConstr (path,s,param)); | PAlias (s,pat) -> let pt , pat = type_pattern ctx h ~h2 set false pat in let t = pvar false s in unify ctx pt t (snd pat); t , PAlias (s,pat) | PTyped (pat,t) -> let pt , pat = type_pattern ctx h ~h2 set add pat in unify ctx pt (type_type ~h:h2 ctx t p) p; pt , PTyped (pat,t) | PStream (l,k) -> let t , polyt = t_poly ctx.gen "stream" in let locals = save_locals ctx in let l = List.map (fun s -> match s with | SPattern pat -> let t , p = type_pattern ctx h ~h2 set true pat in unify ctx t polyt (snd p); SPattern p | SExpr ([v],e) -> let e = type_expr ctx e in let t = pvar true v in unify ctx t e.etype e.epos; SMagicExpr((PIdent v,e.epos),Obj.magic e) | SExpr (vl,e) -> let e = type_expr ctx e in let tl = List.map (pvar true) vl in unify ctx (mk_tup ctx.gen tl) e.etype e.epos; let tup = PTuple (List.map (fun v -> PIdent v, e.epos) vl) in SMagicExpr((tup,e.epos),Obj.magic e) | SMagicExpr _ -> assert false ) l in restore_locals ctx locals; t , PStream (l,k) ) in pt , (pat,p) and type_match ctx t cl p = let ret = t_mono ctx.gen in let cl = List.map (fun (pl,wh,pe) -> let first = ref true in let h = Hashtbl.create 0 in let mainset = ref SSet.empty in let pl = List.map (fun pat -> let set = ref SSet.empty in let pt , pat = type_pattern ctx h set false pat in if !first then begin first := false; mainset := !set; end else begin let s1 = SSet.diff !set !mainset in let s2 = SSet.diff !mainset !set in SSet.iter (fun s -> error (Custom ("Variable " ^ s ^ " must occur in all patterns")) p) (SSet.union s1 s2); end; unify ctx pt t p; pat ) pl in let locals = save_locals ctx in Hashtbl.iter (fun v t -> add_local ctx v t) h; let wh = (match wh with | None -> None | Some e -> let e = type_expr ctx e in unify ctx e.etype t_bool e.epos; Some e ) in let pe = type_expr ctx pe in unify ctx pe.etype ret (pos pe); restore_locals ctx locals; pl , wh , pe ) cl in let rec loop cl = match cl with | TModule(path,TConstr c) :: l -> let path , ut , t = get_constr ctx path c null_pos in if ut == t_error then false else (match tlinks false ut with | TUnion (n,_) -> n = List.length cl | _ -> assert false) | TBool b :: l -> let e = List.exists (fun c -> c = TBool (not b)) l in prerr_endline (if e then "ok" else "notok"); e | TVoid :: _ -> true | _ :: l -> loop cl | [] -> false in Mlmatch.fully_matched_ref := loop; let partial , m = Mlmatch.make cl p in partial , m , ret let modules ctx = let h = Hashtbl.create 0 in Hashtbl.iter (fun p m -> match m.expr with | None -> () | Some e -> let deps = ref (if m.path = ["Core"] || Hashtbl.mem m.deps ["Core"] then [] else [["Core"]]) in let idents = ref [] in Hashtbl.iter (fun _ m -> deps := m.path :: !deps ) m.deps; PMap.iter (fun i t -> idents := i :: !idents ) m.idents; Hashtbl.add h p (e,!deps,!idents) ) ctx.modules; h let open_file ctx file p = let rec loop = function | [] -> error (Custom ("File not found " ^ file)) p | p :: l -> try let f = p ^ file in f , open_in f with _ -> loop l in loop ctx.classpath let load_module ctx m p = try Hashtbl.find ctx.modules m with Not_found -> let file , ch = open_file ctx (String.concat "/" m ^ ".nml") p in let is_core , core = (try false , Hashtbl.find ctx.modules ["Core"] with Not_found -> true , ctx.current ) in let ctx = { ctx with tmptypes = Hashtbl.create 0; functions = []; opens = [core]; current = (if is_core then ctx.current else { path = m; constrs = Hashtbl.create 0; records = Hashtbl.create 0; types = Hashtbl.create 0; expr = None; idents = PMap.empty; deps = Hashtbl.create 0; }) } in Hashtbl.add ctx.modules m ctx.current; let ast = Mlparser.parse (Lexing.from_channel ch) file in if !verbose then print_endline ("Parsed " ^ file); let e = (match ast with | EBlock (e :: l) , p -> let e = type_block ctx e in let el , t = List.fold_left (fun (l,t) e -> let e = type_block ctx e in e :: l , e.etype ) ([e] , e.etype) l in type_functions ctx; mk (TBlock (List.rev el)) t p | _ -> type_expr ctx ast ) in ctx.current.expr <- Some e; if !verbose then print_endline ("Typing done with " ^ file); ctx.current let context cpath = let ctx = { gen = generator(); tmptypes = Hashtbl.create 0; modules = Hashtbl.create 0; functions = []; opens = []; mink = 0; classpath = cpath; curfunction = "anonymous"; current = { path = ["Core"]; expr = None; idents = PMap.empty; constrs = Hashtbl.create 0; types = Hashtbl.create 0; deps = Hashtbl.create 0; records = Hashtbl.create 0; }; } in let add_type args name t = ignore(type_expr ctx (ETypeDecl (args,name,t) , null_pos)); in let add_variable name t = ctx.current.idents <- PMap.add name t ctx.current.idents in add_type [] "bool" EAbstract; add_type ["a"] "list" (EUnion ["[]",None;"::",Some (ETuple [ EPoly "a"; EType (Some (EPoly "a"),[],"list"); ])]); add_variable "neko" (mk_fun ctx.gen [t_polymorph ctx.gen] (t_polymorph ctx.gen)); let core = load_module ctx ["Core"] null_pos in ctx ;; Mlmatch.error_ref := (fun msg p -> error (Custom msg) p); load_module_ref := load_moduleneko-2-4-0/libs/ocaml/nast.ml000066400000000000000000000110131464615675700160130ustar00rootroot00000000000000(* * Neko AST for OCaml * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) type pos = { psource : string; pline : int; } type constant = | True | False | Null | This | Int of int | Float of string | String of string | Builtin of string | Ident of string type while_flag = | NormalWhile | DoWhile type expr_decl = | EConst of constant | EBlock of expr list | EParenthesis of expr | EField of expr * string | ECall of expr * expr list | EArray of expr * expr | EVars of (string * expr option) list | EWhile of expr * expr * while_flag | EIf of expr * expr * expr option | ETry of expr * string * expr | EFunction of string list * expr | EBinop of string * expr * expr | EReturn of expr option | EBreak of expr option | EContinue | ENext of expr * expr | EObject of (string * expr) list | ELabel of string | ESwitch of expr * (expr * expr) list * expr option | ENeko of string and expr = expr_decl * pos let pos = snd let null_pos = { pline = 0; psource = "" } let mk_call v args p = ECall (v,args) , p let mk_call0 v p = ECall (v,[]) , p let mk_call1 v a p = ECall (v,[a]) , p let mk_ident i p = EConst (Ident i) , p let mk_builtin b p = EConst (Builtin b) , p let mk_int i p = EConst (Int i) , p let mk_string s p = EConst (String s) , p let mk_binop op e1 e2 p = EBinop (op,e1,e2) , p let map f (e,p) = (match e with | EBlock el -> EBlock (List.map f el) | EParenthesis e -> EParenthesis (f e) | EField (e,s) -> EField (f e, s) | ECall (e,el) -> ECall (f e, List.map f el) | EArray (e1,e2) -> EArray (f e1, f e2) | EVars vl -> EVars (List.map (fun (v,e) -> v , match e with None -> None | Some e -> Some (f e)) vl) | EWhile (e1,e2,flag) -> EWhile (f e1, f e2, flag) | EIf (e,e1,e2) -> EIf (f e, f e1, match e2 with None -> None | Some e -> Some (f e)) | ETry (e,ident,e2) -> ETry (f e, ident, f e2) | EFunction (params,e) -> EFunction (params, f e) | EBinop (op,e1,e2) -> EBinop (op, f e1, f e2) | EReturn (Some e) -> EReturn (Some (f e)) | EBreak (Some e) -> EBreak (Some (f e)) | ENext (e1,e2) -> ENext (f e1,f e2) | EObject fl -> EObject (List.map (fun (s,e) -> s , f e) fl) | ESwitch (e,cases,def) -> ESwitch (f e,List.map (fun(e1,e2) -> f e1, f e2) cases,match def with None -> None | Some e -> Some (f e)) | EReturn None | EBreak None | EContinue | ENeko _ | ELabel _ | EConst _ as x -> x) , p let iter f (e,p) = match e with | EBlock el -> List.iter f el | EParenthesis e -> f e | EField (e,s) -> f e | ECall (e,el) -> f e; List.iter f el | EArray (e1,e2) -> f e1; f e2 | EVars vl -> List.iter (fun (_,e) -> match e with None -> () | Some e -> f e) vl | EWhile (e1,e2,_) -> f e1; f e2 | EIf (e,e1,e2) -> f e; f e1; (match e2 with None -> () | Some e -> f e) | ETry (e1,_,e2) -> f e1; f e2 | EFunction (_,e) -> f e | EBinop (_,e1,e2) -> f e1; f e2 | EReturn (Some e) -> f e | EBreak (Some e) -> f e | ENext (e1,e2) -> f e1; f e2 | EObject fl -> List.iter (fun (_,e) -> f e) fl | ESwitch (e,cases,def) -> f e; List.iter (fun(e1,e2) -> f e1; f e2) cases; (match def with None -> () | Some e -> f e) | EReturn None | EBreak None | EContinue | ENeko _ | ELabel _ | EConst _ -> () let is_printable c = c >= '\032' && c <= '\126' let escape s = let b = Buffer.create (String.length s) in for i = 0 to (String.length s) - 1 do match s.[i] with | '\n' -> Buffer.add_string b "\\n" | '\t' -> Buffer.add_string b "\\t" | '\r' -> Buffer.add_string b "\\r" | '\\' -> Buffer.add_string b "\\\\" | c when c == '"' || not (is_printable c) -> Buffer.add_string b (Printf.sprintf "\\%.3d" (int_of_char c)) | c -> Buffer.add_char b c done; Buffer.contents b let s_constant = function | True -> "true" | False -> "false" | Null -> "null" | This -> "this" | Int i -> string_of_int i | Float s -> s | String s -> "\"" ^ escape s ^ "\"" | Builtin s -> "$" ^ s | Ident s -> s neko-2-4-0/libs/ocaml/neko/000077500000000000000000000000001464615675700154545ustar00rootroot00000000000000neko-2-4-0/libs/ocaml/neko/bytecode.ml000066400000000000000000000300701464615675700176040ustar00rootroot00000000000000(* * Neko Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) type opcode = (* getters *) | AccNull | AccTrue | AccFalse | AccThis | AccInt of int | AccStack of int | AccGlobal of int | AccEnv of int | AccField of string | AccArray | AccIndex of int | AccBuiltin of string (* setters *) | SetStack of int | SetGlobal of int | SetEnv of int | SetField of string | SetArray | SetIndex of int | SetThis (* stack ops *) | Push | Pop of int | Call of int | ObjCall of int | Jump of int | JumpIf of int | JumpIfNot of int | Trap of int | EndTrap | Ret of int | MakeEnv of int | MakeArray of int (* value ops *) | Bool | IsNull | IsNotNull | Add | Sub | Mult | Div | Mod | Shl | Shr | UShr | Or | And | Xor | Eq | Neq | Gt | Gte | Lt | Lte | Not | TypeOf | Compare | Hash | New type global = | GlobalVar of string | GlobalFunction of int * int | GlobalString of string | GlobalFloat of string exception Invalid_file let trap_stack_delta = 5 let max_call_args = 5 let hash_field s = let acc = ref 0 in for i = 0 to String.length s - 1 do acc := 223 * !acc + Char.code (String.unsafe_get s i) done; acc := !acc land ((1 lsl 31) - 1); !acc let op_param = function | AccInt _ | AccStack _ | AccGlobal _ | AccEnv _ | AccField _ | AccBuiltin _ | SetStack _ | SetGlobal _ | SetEnv _ | SetField _ | Pop _ | Call _ | ObjCall _ | Jump _ | JumpIf _ | JumpIfNot _ | Trap _ | MakeEnv _ | MakeArray _ | Ret _ | AccIndex _ | SetIndex _ -> true | AccNull | AccTrue | AccFalse | AccThis | AccArray | SetArray | SetThis | Push | EndTrap | Bool | Add | Sub | Mult | Div | Mod | Shl | Shr | UShr | Or | And | Xor | Eq | Neq | Gt | Gte | Lt | Lte | IsNull | IsNotNull | Not | TypeOf | Compare | Hash | New -> false let code_tables ops = let ids = Hashtbl.create 0 in Array.iter (function | AccField s | SetField s | AccBuiltin s -> let id = hash_field s in (try let f = Hashtbl.find ids id in if f <> s then failwith ("Field hashing conflict " ^ s ^ " and " ^ f); with Not_found -> Hashtbl.add ids id s) | _ -> () ) ops; let p = ref 0 in let pos = Array.create (Array.length ops + 1) 0 in Array.iteri (fun i op -> Array.unsafe_set pos i !p; p := !p + (if op_param op then 2 else 1); ) ops; Array.unsafe_set pos (Array.length ops) !p; ids , pos , !p let write ch (globals,ops) = IO.nwrite ch "NEKO"; let globals = DynArray.of_array globals in let ids , pos , csize = code_tables ops in IO.write_i32 ch (DynArray.length globals); IO.write_i32 ch (Hashtbl.length ids); IO.write_i32 ch csize; DynArray.iter (function | GlobalVar s -> IO.write_byte ch 1; IO.nwrite ch s; IO.write ch '\000'; | GlobalFunction (p,nargs) -> IO.write_byte ch 2; IO.write_i32 ch (pos.(p) lor (nargs lsl 24)) | GlobalString s -> IO.write_byte ch 3; IO.write_ui16 ch (String.length s); IO.nwrite ch s | GlobalFloat s -> IO.write_byte ch 4; IO.nwrite ch s; IO.write ch '\000' ) globals; Hashtbl.iter (fun _ s -> IO.nwrite ch s; IO.write ch '\000'; ) ids; Array.iteri (fun i op -> let pop = ref None in let opid = (match op with | AccNull -> 0 | AccTrue -> 1 | AccFalse -> 2 | AccThis -> 3 | AccInt n -> pop := Some n; 4 | AccStack n -> pop := Some n; 5 | AccGlobal n -> pop := Some n; 6 | AccEnv n -> pop := Some n; 7 | AccField s -> pop := Some (hash_field s); 8 | AccArray -> 9 | AccIndex n -> pop := Some n; 10 | AccBuiltin s -> pop := Some (hash_field s); 11 | SetStack n -> pop := Some n; 12 | SetGlobal n -> pop := Some n; 13 | SetEnv n -> pop := Some n; 14 | SetField s -> pop := Some (hash_field s); 15 | SetArray -> 16 | SetIndex n -> pop := Some n; 17 | SetThis -> 18 | Push -> 19 | Pop n -> pop := Some n; 20 | Call n -> pop := Some n; 21 | ObjCall n -> pop := Some n; 22 | Jump n -> pop := Some (pos.(i+n) - pos.(i)); 23 | JumpIf n -> pop := Some (pos.(i+n) - pos.(i)); 24 | JumpIfNot n -> pop := Some (pos.(i+n) - pos.(i)); 25 | Trap n -> pop := Some (pos.(i+n) - pos.(i)); 26 | EndTrap -> 27 | Ret n -> pop := Some n; 28 | MakeEnv n -> pop := Some n; 29 | MakeArray n -> pop := Some n; 30 | Bool -> 31 | IsNull -> 32 | IsNotNull -> 33 | Add -> 34 | Sub -> 35 | Mult -> 36 | Div -> 37 | Mod -> 38 | Shl -> 39 | Shr -> 40 | UShr -> 41 | Or -> 42 | And -> 43 | Xor -> 44 | Eq -> 45 | Neq -> 46 | Gt -> 47 | Gte -> 48 | Lt -> 49 | Lte -> 50 | Not -> 51 | TypeOf -> 52 | Compare -> 53 | Hash -> 54 | New -> 55 ) in match !pop with | None -> IO.write_byte ch (opid lsl 2) | Some n when opid < 32 && (n = 0 || n = 1) -> IO.write_byte ch ((opid lsl 3) lor (n lsl 2) lor 1) | Some n when n >= 0 && n <= 0xFF -> IO.write_byte ch ((opid lsl 2) lor 2); IO.write_byte ch n | Some n -> IO.write_byte ch ((opid lsl 2) lor 3); IO.write_i32 ch n ) ops let read_string ch = let b = Buffer.create 5 in let rec loop() = let c = IO.read ch in if c = '\000' then Buffer.contents b else begin Buffer.add_char b c; loop() end; in loop() let read ch = try let head = IO.nread ch 4 in if head <> "NEKO" then raise Invalid_file; let nglobals = IO.read_i32 ch in let nids = IO.read_i32 ch in let csize = IO.read_i32 ch in if nglobals < 0 || nglobals > 0xFFFF || nids < 0 || nids > 0xFFFF || csize < 0 || csize > 0xFFFFFF then raise Invalid_file; let globals = Array.init nglobals (fun _ -> match IO.read_byte ch with | 1 -> GlobalVar (read_string ch) | 2 -> let v = IO.read_i32 ch in GlobalFunction (v land 0xFFFFFF, v lsr 24) | 3 -> let len = IO.read_ui16 ch in GlobalString (IO.nread ch len) | 4 -> GlobalFloat (read_string ch) | _ -> raise Invalid_file ) in let ids = Hashtbl.create 0 in let rec loop n = if n = 0 then () else let s = read_string ch in let id = hash_field s in try let s2 = Hashtbl.find ids id in if s <> s2 then raise Invalid_file; with Not_found -> Hashtbl.add ids id s; loop (n-1) in loop nids; let pos = Array.create (csize+1) (-1) in let cpos = ref 0 in let jumps = ref [] in let ops = DynArray.create() in while !cpos < csize do let code = IO.read_byte ch in let op , p = (match code land 3 with | 0 -> code lsr 2 , 0 | 1 -> code lsr 3 , ((code lsr 2) land 1) | 2 -> code lsr 2 , IO.read_byte ch | 3 -> code lsr 2 , IO.read_i32 ch | _ -> assert false ) in let op = (match op with | 0 -> AccNull | 1 -> AccTrue | 2 -> AccFalse | 3 -> AccThis | 4 -> AccInt p | 5 -> AccStack p | 6 -> AccGlobal p | 7 -> AccEnv p | 8 -> AccField (try Hashtbl.find ids p with Not_found -> raise Invalid_file) | 9 -> AccArray | 10 -> AccIndex p | 11 -> AccBuiltin (try Hashtbl.find ids p with Not_found -> raise Invalid_file) | 12 -> SetStack p | 13 -> SetGlobal p | 14 -> SetEnv p | 15 -> SetField (try Hashtbl.find ids p with Not_found -> raise Invalid_file) | 16 -> SetArray | 17 -> SetIndex p | 18 -> SetThis | 19 -> Push | 20 -> Pop p | 21 -> Call p | 22 -> ObjCall p | 23 -> jumps := (!cpos , DynArray.length ops) :: !jumps; Jump p | 24 -> jumps := (!cpos , DynArray.length ops) :: !jumps; JumpIf p | 25 -> jumps := (!cpos , DynArray.length ops) :: !jumps; JumpIfNot p | 26 -> jumps := (!cpos , DynArray.length ops) :: !jumps; Trap p | 27 -> EndTrap | 28 -> Ret p | 29 -> MakeEnv p | 30 -> MakeArray p | 31 -> Bool | 32 -> IsNull | 33 -> IsNotNull | 34 -> Add | 35 -> Sub | 36 -> Mult | 37 -> Div | 38 -> Mod | 39 -> Shl | 40 -> Shr | 41 -> UShr | 42 -> Or | 43 -> And | 44 -> Xor | 45 -> Eq | 46 -> Neq | 47 -> Gt | 48 -> Gte | 49 -> Lt | 50 -> Lte | 51 -> Not | 52 -> TypeOf | 53 -> Compare | 54 -> Hash | 55 -> New | _ -> raise Invalid_file ) in pos.(!cpos) <- DynArray.length ops; cpos := !cpos + (if op_param op then 2 else 1); DynArray.add ops op; done; if !cpos <> csize then raise Invalid_file; pos.(!cpos) <- DynArray.length ops; let pos_index i sadr = let idx = pos.(sadr) in if idx = -1 then raise Invalid_file; idx - i in List.iter (fun (a,i) -> DynArray.set ops i (match DynArray.get ops i with | Jump p -> Jump (pos_index i (a+p)) | JumpIf p -> JumpIf (pos_index i (a+p)) | JumpIfNot p -> JumpIfNot (pos_index i (a+p)) | Trap p -> Trap (pos_index i (a+p)) | _ -> assert false) ) !jumps; Array.iteri (fun i g -> match g with | GlobalFunction (f,n) -> globals.(i) <- GlobalFunction (pos_index 0 f,n) | _ -> () ) globals; globals , DynArray.to_array ops with | IO.No_more_input | IO.Overflow _ -> raise Invalid_file let escape str = String.escaped str let dump ch (globals,ops) = let ids, pos , csize = code_tables ops in IO.printf ch "nglobals : %d\n" (Array.length globals); IO.printf ch "nfields : %d\n" (Hashtbl.length ids); IO.printf ch "codesize : %d ops , %d total\n" (Array.length ops) csize; IO.printf ch "GLOBALS =\n"; let marks = Array.create csize false in Array.iteri (fun i g -> IO.printf ch " global %d : %s\n" i (match g with | GlobalVar s -> "var " ^ s | GlobalFunction (p,n) -> if p >= 0 && p < csize then marks.(p) <- true; "function " ^ string_of_int p ^ " nargs " ^ string_of_int n | GlobalString s -> "string \"" ^ escape s ^ "\"" | GlobalFloat s -> "float " ^ s) ) globals; IO.printf ch "FIELDS =\n"; Hashtbl.iter (fun h f -> IO.printf ch " %s%s%.8X\n" f (if String.length f >= 24 then " " else String.make (24 - String.length f) ' ') h; ) ids; IO.printf ch "CODE =\n"; let str s i = s ^ " " ^ string_of_int i in let bpos = ref 0 in Array.iteri (fun pos op -> if marks.(pos) then IO.write ch '\n'; IO.printf ch "%.6X %6d %s\n" (!bpos) pos (match op with | AccNull -> "AccNull" | AccTrue -> "AccTrue" | AccFalse -> "AccFalse" | AccThis -> "AccThis" | AccInt i -> str "AccInt" i | AccStack i -> str "AccStack" i | AccGlobal i -> str "AccGlobal" i | AccEnv i -> str "AccEnv" i | AccField s -> "AccField " ^ s | AccArray -> "AccArray" | AccIndex i -> str "AccIndex" i | AccBuiltin s -> "AccBuiltin " ^ s | SetStack i -> str "SetStack" i | SetGlobal i -> str "SetGlobal" i | SetEnv i -> str "SetEnv" i | SetField f -> "SetField " ^ f | SetArray -> "SetArray" | SetIndex i -> str "SetIndex" i | SetThis -> "SetThis" | Push -> "Push" | Pop i -> str "Pop" i | Call i -> str "Call" i | ObjCall i -> str "ObjCall" i | Jump i -> str "Jump" (pos + i) | JumpIf i -> str "JumpIf" (pos + i) | JumpIfNot i -> str "JumpIfNot" (pos + i) | Trap i -> str "Trap" (pos + i) | EndTrap -> "EndTrap" | Ret i -> str "Ret" i | MakeEnv i -> str "MakeEnv" i | MakeArray i -> str "MakeArray" i | Bool -> "Bool" | IsNull -> "IsNull" | IsNotNull -> "IsNotNull" | Add -> "Add" | Sub -> "Sub" | Mult -> "Mult" | Div -> "Div" | Mod -> "Mod" | Shl -> "Shl" | Shr -> "Shr" | UShr -> "UShr" | Or -> "Or" | And -> "And" | Xor -> "Xor" | Eq -> "Eq" | Neq -> "Neq" | Gt -> "Gt" | Gte -> "Gte" | Lt -> "Lt" | Lte -> "Lte" | Not -> "Not" | TypeOf -> "TypeOf" | Compare -> "Compare" | Hash -> "Hash" | New -> "New" ); bpos := !bpos + if op_param op then 2 else 1; ) ops; IO.printf ch "END\n" neko-2-4-0/libs/ocaml/neko/compile.ml000066400000000000000000000423311464615675700174410ustar00rootroot00000000000000(* * Neko Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Ast open Bytecode type label = { lname : string; ltraps : int; lstack : int; mutable lpos : int option; mutable lwait : (unit -> unit) list; } type context = { mutable ops : opcode DynArray.t; mutable locals : (string,int) PMap.t; globals : (global,int) Hashtbl.t; gobjects : (string list,int) Hashtbl.t; mutable env : (string,int) PMap.t; mutable nenv : int; mutable stack : int; mutable loop_limit : int; mutable limit : int; mutable ntraps : int; mutable breaks : ((unit -> unit) * pos) list; mutable continues : ((unit -> unit) * pos) list; mutable functions : (opcode DynArray.t * int * int) list; mutable gtable : global DynArray.t; labels : (string,label) Hashtbl.t; } type error_msg = | Custom of string exception Error of error_msg * pos let error e p = raise (Error (e,p)) let error_msg = function | Custom s -> s let stack_delta = function | AccNull | AccTrue | AccFalse | AccThis | AccInt _ | AccStack _ | AccGlobal _ | AccEnv _ | AccField _ | AccBuiltin _ | AccIndex _ | JumpIf _ | JumpIfNot _ | Jump _ | Ret _ | SetGlobal _ | SetStack _ | SetEnv _ | SetThis | Bool | EndTrap | IsNull | IsNotNull | Not | Hash | TypeOf | New -> 0 | Add | Sub | Mult | Div | Mod | Shl | Shr | UShr | Or | And | Xor | Eq | Neq | Gt | Gte | Lt | Lte -> -1 | AccArray -> -1 | SetField _ | SetIndex _ | Compare -> -1 | SetArray -> -2 | Push -> 1 | Pop x -> -x | Call nargs -> -nargs | ObjCall nargs -> -(nargs + 1) | MakeEnv size | MakeArray size -> -size | Trap _ -> trap_stack_delta let pos ctx = DynArray.length ctx.ops let write ctx op = ctx.stack <- ctx.stack + stack_delta op; DynArray.add ctx.ops op let jmp ?cond ctx = let p = pos ctx in write ctx (Jump 0); (fun() -> DynArray.set ctx.ops p (match cond with | None -> Jump (pos ctx - p) | Some true -> JumpIf (pos ctx - p) | Some false -> JumpIfNot (pos ctx -p)) ) let goto ctx p = write ctx (Jump (p - pos ctx)) let global ctx g = try Hashtbl.find ctx.globals g with Not_found -> let gid = DynArray.length ctx.gtable in Hashtbl.add ctx.globals g gid; DynArray.add ctx.gtable g; gid let save_breaks ctx = let oldc = ctx.continues in let oldb = ctx.breaks in let oldl = ctx.loop_limit in ctx.loop_limit <- ctx.stack; ctx.breaks <- []; ctx.continues <- []; ctx , oldc, oldb , oldl let process_continues (ctx,oldc,_,_) = List.iter (fun (f,_) -> f()) ctx.continues; ctx.continues <- oldc let process_breaks (ctx,_,oldb,oldl) = List.iter (fun (f,_) -> f()) ctx.breaks; ctx.loop_limit <- oldl; ctx.breaks <- oldb let check_breaks ctx = List.iter (fun (_,p) -> error (Custom "Break outside a loop") p) ctx.breaks; List.iter (fun (_,p) -> error (Custom "Continue outside a loop") p) ctx.continues let rec scan_labels ctx supported e = match fst e with | EFunction (args,e) -> let nargs = List.length args in let ntraps = ctx.ntraps in ctx.ntraps <- 0; ctx.stack <- ctx.stack + nargs; scan_labels ctx supported e; ctx.stack <- ctx.stack - nargs; ctx.ntraps <- ntraps | EBlock _ -> let old = ctx.stack in Ast.iter (scan_labels ctx supported) e; ctx.stack <- old | EVars l -> List.iter (fun (_,e) -> (match e with | None -> () | Some e -> scan_labels ctx supported e); ctx.stack <- ctx.stack + 1 ) l | ELabel l when not supported -> error (Custom "Label is not supported in this part of the program") (snd e); | ELabel l when Hashtbl.mem ctx.labels l -> error (Custom ("Duplicate label " ^ l)) (snd e) | ELabel l -> Hashtbl.add ctx.labels l { lname = l; ltraps = ctx.ntraps; lstack = ctx.stack; lpos = None; lwait = []; } | ETry (e,_,e2) -> ctx.stack <- ctx.stack + trap_stack_delta; ctx.ntraps <- ctx.ntraps + 1; scan_labels ctx supported e; ctx.stack <- ctx.stack - trap_stack_delta; ctx.ntraps <- ctx.ntraps - 1; ctx.stack <- ctx.stack + 1; scan_labels ctx supported e2; ctx.stack <- ctx.stack - 1; | EBinop ("=",e1,e2) -> let rec is_extended (e,_) = match e with | EParenthesis e -> is_extended e | EArray _ | EField _ -> true | _ -> false in let ext = is_extended e1 in if ext then ctx.stack <- ctx.stack + 1; scan_labels ctx supported e2; ctx.stack <- ctx.stack + 1; scan_labels ctx supported e1; ctx.stack <- ctx.stack - (if ext then 2 else 1); | ECall ((EConst (Builtin x),_),el) when x <> "array" && x <> "apply" -> Ast.iter (scan_labels ctx false) e | ECall (_,el) -> List.iter (fun e -> scan_labels ctx supported e; ctx.stack <- ctx.stack + 1; ) el; ctx.stack <- ctx.stack - List.length el | EObject fl -> ctx.stack <- ctx.stack + 2; List.iter (fun (s,e) -> scan_labels ctx supported e ) fl; ctx.stack <- ctx.stack - 2; | EConst _ | EContinue | EBreak _ | EReturn _ | EIf _ | EWhile _ | EParenthesis _ | ENext _ -> Ast.iter (scan_labels ctx supported) e | EBinop _ | EArray _ | EField _ -> Ast.iter (scan_labels ctx false) e let compile_constant ctx c p = match c with | True -> write ctx AccTrue | False -> write ctx AccFalse | Null -> write ctx AccNull | This -> write ctx AccThis | Int n -> write ctx (AccInt n) | Float f -> write ctx (AccGlobal (global ctx (GlobalFloat f))) | String s -> write ctx (AccGlobal (global ctx (GlobalString s))) | Builtin s -> (match s with | "tnull" -> write ctx (AccInt 0) | "tint" -> write ctx (AccInt 1) | "tfloat" -> write ctx (AccInt 2) | "tbool" -> write ctx (AccInt 3) | "tstring" -> write ctx (AccInt 4) | "tobject" -> write ctx (AccInt 5) | "tarray" -> write ctx (AccInt 6) | "tfunction" -> write ctx (AccInt 7) | "tabstract" -> write ctx (AccInt 8) | s -> write ctx (AccBuiltin s)) | Ident s -> try let e = PMap.find s ctx.env in write ctx (AccEnv e); with Not_found -> try let l = PMap.find s ctx.locals in if l <= ctx.limit then begin let e = ctx.nenv in ctx.nenv <- ctx.nenv + 1; ctx.env <- PMap.add s e ctx.env; write ctx (AccEnv e); end else write ctx (AccStack (ctx.stack - l)); with Not_found -> let g = global ctx (GlobalVar s) in write ctx (AccGlobal g) let rec compile_binop ctx op e1 e2 p = match op with | "=" -> (match fst e1 with | EConst (Ident s) -> compile ctx e2; (try let e = PMap.find s ctx.env in write ctx (SetEnv e); with Not_found -> try let l = PMap.find s ctx.locals in if l <= ctx.limit then begin let e = ctx.nenv in ctx.nenv <- ctx.nenv + 1; ctx.env <- PMap.add s e ctx.env; write ctx (SetEnv e); end else write ctx (SetStack (ctx.stack - l)) with Not_found -> let g = global ctx (GlobalVar s) in write ctx (SetGlobal g)) | EField (e,f) -> compile ctx e; write ctx Push; compile ctx e2; write ctx (SetField f) | EArray (e1,(EConst (Int n),_)) -> compile ctx e1; write ctx Push; compile ctx e2; write ctx (SetIndex n) | EArray (ea,ei) -> compile ctx ei; write ctx Push; compile ctx ea; write ctx Push; compile ctx e2; write ctx SetArray | EConst This -> compile ctx e2; write ctx SetThis | _ -> error (Custom "Invalid assign") p) | "&&" -> compile ctx e1; let jnext = jmp ~cond:false ctx in compile ctx e2; jnext() | "||" -> compile ctx e1; let jnext = jmp ~cond:true ctx in compile ctx e2; jnext() | _ -> match op , e1 , e2 with | "==" , _ , (EConst Null,_) -> compile ctx e1; write ctx IsNull | "!=" , _ , (EConst Null,_) -> compile ctx e1; write ctx IsNotNull | "==" , (EConst Null,_) , _ -> compile ctx e2; write ctx IsNull | "!=" , (EConst Null,_) , _ -> compile ctx e2; write ctx IsNotNull | _ -> compile ctx e1; write ctx Push; compile ctx e2; match op with | "+" -> write ctx Add | "-" -> write ctx Sub | "/" -> write ctx Div | "*" -> write ctx Mult | "%" -> write ctx Mod | "<<" -> write ctx Shl | ">>" -> write ctx Shr | ">>>" -> write ctx UShr | "|" -> write ctx Or | "&" -> write ctx And | "^" -> write ctx Xor | "==" -> write ctx Eq | "!=" -> write ctx Neq | ">" -> write ctx Gt | ">=" -> write ctx Gte | "<" -> write ctx Lt | "<=" -> write ctx Lte | _ -> error (Custom "Unknown operation") p and compile_function ctx params e = let limit = ctx.limit in let ops = ctx.ops in let breaks = ctx.breaks in let continues = ctx.continues in let locals = ctx.locals in let env = ctx.env in let nenv = ctx.nenv in let ntraps = ctx.ntraps in ctx.ops <- DynArray.create(); ctx.breaks <- []; ctx.continues <- []; ctx.env <- PMap.empty; ctx.nenv <- 0; ctx.ntraps <- 0; ctx.limit <- ctx.stack; List.iter (fun v -> ctx.stack <- ctx.stack + 1; ctx.locals <- PMap.add v ctx.stack ctx.locals; ) params; let s = ctx.stack in compile ctx e; write ctx (Ret (ctx.stack - ctx.limit)); assert( ctx.stack = s ); check_breaks ctx; ctx.stack <- ctx.limit; ctx.limit <- limit; ctx.breaks <- breaks; ctx.continues <- continues; ctx.locals <- locals; let gid = DynArray.length ctx.gtable in ctx.functions <- (ctx.ops,gid,List.length params) :: ctx.functions; DynArray.add ctx.gtable (GlobalFunction (gid,-1)); ctx.ops <- ops; let local_env = ctx.env in let local_nenv = ctx.nenv in ctx.env <- env; ctx.ntraps <- ntraps; ctx.nenv <- nenv; if local_nenv > 0 then begin let a = Array.create local_nenv "" in PMap.iter (fun v i -> a.(i) <- v) local_env; Array.iter (fun v -> compile_constant ctx (Ident v) null_pos; write ctx Push; ) a; write ctx (AccGlobal gid); write ctx (MakeEnv local_nenv); end else write ctx (AccGlobal gid); and compile_builtin ctx b el p = match b , el with | "istrue" , [e] -> compile ctx e; write ctx Bool | "not" , [e] -> compile ctx e; write ctx Not | "typeof" , [e] -> compile ctx e; write ctx TypeOf | "hash" , [e] -> compile ctx e; write ctx Hash | "compare" , [e1;e2] -> compile ctx e1; write ctx Push; compile ctx e2; write ctx Compare | "goto" , [ EConst (Ident l) , _ ] -> let l = (try Hashtbl.find ctx.labels l with Not_found -> error (Custom ("Unknown label " ^ l)) p) in let os = ctx.stack in let ntraps = ref ctx.ntraps in let etraps = ref [] in while !ntraps > l.ltraps do write ctx EndTrap; ctx.stack <- ctx.stack - trap_stack_delta; ntraps := !ntraps - 1; done; while !ntraps < l.ltraps do etraps := (pos ctx) :: !etraps; write ctx (Trap 0); ntraps := !ntraps + 1; done; if ctx.stack > l.lstack then write ctx (Pop (ctx.stack - l.lstack)); while ctx.stack < l.lstack do write ctx Push; done; ctx.stack <- os; (match l.lpos with | None -> l.lwait <- jmp ctx :: l.lwait | Some p -> write ctx (Jump p)); if !etraps <> [] then begin List.iter (fun p -> DynArray.set ctx.ops p (Trap (pos ctx - p)); ) !etraps; write ctx Push; compile_constant ctx (Builtin "throw") p; write ctx (Call 1); end; | "goto" , _ -> error (Custom "Invalid $goto statement") p | _ -> List.iter (fun e -> compile ctx e; write ctx Push; ) el; compile_constant ctx (Builtin b) p; write ctx (Call (List.length el)) and compile ctx (e,p) = match e with | EConst c -> compile_constant ctx c p | EBlock [] -> write ctx AccNull | EBlock el -> let locals = ctx.locals in let stack = ctx.stack in List.iter (compile ctx) el; if stack < ctx.stack then write ctx (Pop (ctx.stack - stack)); assert( stack = ctx.stack ); ctx.locals <- locals | EParenthesis e -> compile ctx e | EField (e,f) -> compile ctx e; write ctx (AccField f) | ECall ((EConst (Builtin "array"),_),el) -> List.iter (fun e -> compile ctx e; write ctx Push; ) el; write ctx (MakeArray (List.length el)); | ECall (_,el) when List.length el > max_call_args -> error (Custom "Too many arguments") p | ECall ((EField (e,f),_),el) -> List.iter (fun e -> compile ctx e; write ctx Push; ) el; compile ctx e; write ctx Push; write ctx (AccField f); write ctx (ObjCall (List.length el)) | ECall ((EConst (Builtin b),_),el) -> compile_builtin ctx b el p | ECall (e,el) -> List.iter (fun e -> compile ctx e; write ctx Push; ) el; compile ctx e; write ctx (Call (List.length el)) | EArray (e1,(EConst (Int n),_)) -> compile ctx e1; write ctx (AccIndex n) | EArray (e1,e2) -> compile ctx e1; write ctx Push; compile ctx e2; write ctx AccArray | EVars vl -> List.iter (fun (v,o) -> (match o with | None -> write ctx AccNull | Some e -> compile ctx e); write ctx Push; ctx.locals <- PMap.add v ctx.stack ctx.locals; ) vl | EWhile (econd,e,NormalWhile) -> let start = pos ctx in compile ctx econd; let jend = jmp ~cond:false ctx in let save = save_breaks ctx in compile ctx e; process_continues save; goto ctx start; process_breaks save; jend(); | EWhile (econd,e,DoWhile) -> let start = pos ctx in let save = save_breaks ctx in compile ctx e; process_continues save; compile ctx econd; write ctx (JumpIf (start - pos ctx)); process_breaks save | EIf (e,e1,e2) -> let stack = ctx.stack in compile ctx e; let jelse = jmp ~cond:false ctx in compile ctx e1; assert( stack = ctx.stack ); (match e2 with | None -> jelse() | Some e2 -> let jend = jmp ctx in jelse(); compile ctx e2; assert( stack = ctx.stack ); jend()); | ETry (e,v,ecatch) -> let start = pos ctx in write ctx (Trap 0); ctx.ntraps <- ctx.ntraps + 1; compile ctx e; ctx.ntraps <- ctx.ntraps - 1; ctx.stack <- ctx.stack - trap_stack_delta; write ctx EndTrap; let jend = jmp ctx in DynArray.set ctx.ops start (Trap (pos ctx - start)); write ctx Push; let locals = ctx.locals in ctx.locals <- PMap.add v ctx.stack ctx.locals; compile ctx ecatch; write ctx (Pop 1); ctx.locals <- locals; jend() | EBinop ("-",(EConst (Int 0),_),(EConst (Int i),_)) -> compile ctx (EConst (Int (-i)),p) | EBinop (op,e1,e2) -> compile_binop ctx op e1 e2 p | EReturn None -> write ctx AccNull; for i = 1 to ctx.ntraps do write ctx EndTrap; done; write ctx (Ret (ctx.stack - ctx.limit)); | EReturn (Some e) -> compile ctx e; for i = 1 to ctx.ntraps do write ctx EndTrap; done; write ctx (Ret (ctx.stack - ctx.limit - ctx.ntraps * trap_stack_delta)); | EBreak e -> assert (ctx.ntraps = 0); (match e with | None -> () | Some e -> compile ctx e); if ctx.loop_limit <> ctx.stack then DynArray.add ctx.ops (Pop (ctx.stack - ctx.loop_limit)); ctx.breaks <- (jmp ctx , p) :: ctx.breaks | EContinue -> assert (ctx.ntraps = 0); if ctx.loop_limit <> ctx.stack then DynArray.add ctx.ops (Pop (ctx.stack - ctx.loop_limit)); ctx.continues <- (jmp ctx , p) :: ctx.continues | EFunction (params,e) -> compile_function ctx params e | ENext (e1,e2) -> compile ctx e1; compile ctx e2 | EObject [] -> write ctx AccNull; write ctx New | EObject fl -> let fields = List.sort compare (List.map fst fl) in let id = (try Hashtbl.find ctx.gobjects fields with Not_found -> let id = global ctx (GlobalVar ("o:" ^ string_of_int (Hashtbl.length ctx.gobjects))) in Hashtbl.add ctx.gobjects fields id; id ) in write ctx (AccGlobal id); write ctx New; write ctx Push; List.iter (fun (f,e) -> write ctx Push; compile ctx e; write ctx (SetField f); write ctx (AccStack 0); ) fl; write ctx (Pop 1) | ELabel l -> let l = (try Hashtbl.find ctx.labels l with Not_found -> assert false) in if ctx.stack <> l.lstack then assert false; if ctx.ntraps <> l.ltraps then assert false; List.iter (fun f -> f()) l.lwait; l.lwait <- []; l.lpos <- Some (pos ctx) let compile file ast = let ctx = { stack = 0; loop_limit = 0; limit = -1; globals = Hashtbl.create 0; gobjects = Hashtbl.create 0; gtable = DynArray.create(); locals = PMap.empty; ops = DynArray.create(); breaks = []; continues = []; functions = []; env = PMap.empty; nenv = 0; ntraps = 0; labels = Hashtbl.create 0; } in scan_labels ctx true ast; compile ctx ast; check_breaks ctx; if ctx.functions <> [] || Hashtbl.length ctx.gobjects <> 0 then begin let ctxops = ctx.ops in let ops = DynArray.create() in ctx.ops <- ops; write ctx (Jump 0); List.iter (fun (fops,gid,nargs) -> DynArray.set ctx.gtable gid (GlobalFunction (DynArray.length ops,nargs)); DynArray.append fops ops; ) (List.rev ctx.functions); DynArray.set ops 0 (Jump (DynArray.length ops)); Hashtbl.iter (fun fl g -> write ctx AccNull; write ctx New; write ctx (SetGlobal g); List.iter (fun f -> write ctx (AccGlobal g); write ctx Push; write ctx (SetField f); ) fl ) ctx.gobjects; DynArray.append ctxops ops; end; DynArray.to_array ctx.gtable, DynArray.to_array ctx.ops neko-2-4-0/libs/ocaml/neko/lexer.mll000066400000000000000000000135631464615675700173110ustar00rootroot00000000000000(* * Neko Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) { open Ast open Lexing type error_msg = | Invalid_character of char | Unterminated_string | Unclosed_comment | Invalid_escaped_character of int | Invalid_escape exception Error of error_msg * pos let error_msg = function | Invalid_character c when int_of_char c > 32 && int_of_char c < 128 -> Printf.sprintf "Invalid character '%c'" c | Invalid_character c -> Printf.sprintf "Invalid character 0x%.2X" (int_of_char c) | Unterminated_string -> "Unterminated string" | Unclosed_comment -> "Unclosed comment" | Invalid_escaped_character n -> Printf.sprintf "Invalid escaped character %d" n | Invalid_escape -> "Invalid escape sequence" let cur_file = ref "" let all_lines = Hashtbl.create 0 let lines = ref [] let buf = Buffer.create 100 let error e pos = raise (Error (e,{ pmin = pos; pmax = pos; pfile = !cur_file })) let keywords = let h = Hashtbl.create 3 in List.iter (fun k -> Hashtbl.add h (s_keyword k) k) [Var;While;Do;If;Else;Function;Return;Break;Continue;Try;Catch] ; h let init file = cur_file := file; lines := [] let save_lines() = Hashtbl.replace all_lines !cur_file !lines let save() = save_lines(); !cur_file let restore file = save_lines(); cur_file := file; lines := Hashtbl.find all_lines file let newline lexbuf = lines := (lexeme_end lexbuf) :: !lines let find_line p lines = let rec loop n delta = function | [] -> n , p - delta | lp :: l when lp > p -> n , p - delta | lp :: l -> loop (n+1) lp l in loop 1 0 lines let get_error_line p = let lines = List.rev (try Hashtbl.find all_lines p.pfile with Not_found -> []) in let l, _ = find_line p.pmin lines in l let get_error_pos printer p = if p.pmin = -1 then "(unknown)" else let lines = List.rev (try Hashtbl.find all_lines p.pfile with Not_found -> []) in let l1, p1 = find_line p.pmin lines in let l2, p2 = find_line p.pmax lines in if l1 = l2 then begin let s = (if p1 = p2 then Printf.sprintf " %d" p1 else Printf.sprintf "s %d-%d" p1 p2) in Printf.sprintf "%s character%s" (printer p.pfile l1) s end else Printf.sprintf "%s lines %d-%d" (printer p.pfile l1) l1 l2 let reset() = Buffer.reset buf let contents() = Buffer.contents buf let store lexbuf = Buffer.add_string buf (lexeme lexbuf) let add c = Buffer.add_string buf c let mk_tok t pmin pmax = t , { pfile = !cur_file; pmin = pmin; pmax = pmax } let mk lexbuf t = mk_tok t (lexeme_start lexbuf) (lexeme_end lexbuf) let mk_ident lexbuf = let s = lexeme lexbuf in mk lexbuf (try Keyword (Hashtbl.find keywords s) with Not_found -> Const (Ident s)) } let ident = ['a'-'z' 'A'-'Z' '_' '@'] ['a'-'z' 'A'-'Z' '0'-'9' '_' '@']* let binop = ['!' '=' '*' '/' '<' '>' '&' '|' '^' '%' '+' ':' '-'] let number = ['0'-'9'] rule token = parse | eof { mk lexbuf Eof } | ';' { mk lexbuf Semicolon } | '.' { mk lexbuf Dot } | ',' { mk lexbuf Comma } | '{' { mk lexbuf BraceOpen } | '}' { mk lexbuf BraceClose } | '(' { mk lexbuf ParentOpen } | ')' { mk lexbuf ParentClose } | '[' { mk lexbuf BracketOpen } | ']' { mk lexbuf BracketClose } | "=>" { mk lexbuf Arrow } | [' ' '\r' '\t']+ { token lexbuf } | '\n' { newline lexbuf; token lexbuf } | "0x" ['0'-'9' 'a'-'f' 'A'-'F']+ | number+ { mk lexbuf (Const (Int (int_of_string (lexeme lexbuf)))) } | number+ '.' number* | '.' number+ { mk lexbuf (Const (Float (lexeme lexbuf))) } | '$' (ident as v) { mk lexbuf (Const (Builtin v)) } | "true" { mk lexbuf (Const True) } | "false" { mk lexbuf (Const False) } | "null" { mk lexbuf (Const Null) } | "this" { mk lexbuf (Const This) } | ident { mk_ident lexbuf } | '"' { reset(); let pmin = lexeme_start lexbuf in let pmax = (try string lexbuf with Exit -> error Unterminated_string pmin) in mk_tok (Const (String (contents()))) pmin pmax; } | "/*" { reset(); let pmin = lexeme_start lexbuf in let pmax = (try comment lexbuf with Exit -> error Unclosed_comment pmin) in mk_tok (Comment (contents())) pmin pmax; } | "//" [^'\n']* { let s = lexeme lexbuf in let n = (if s.[String.length s - 1] = '\r' then 3 else 2) in mk lexbuf (CommentLine (String.sub s 2 ((String.length s)-n))) } | binop binop? | ">>>" { mk lexbuf (Binop (lexeme lexbuf)) } | _ { error (Invalid_character (lexeme_char lexbuf 0)) (lexeme_start lexbuf) } and comment = parse | eof { raise Exit } | '\r' { comment lexbuf } | '\n' { newline lexbuf; store lexbuf; comment lexbuf } | "*/" { lexeme_end lexbuf } | '*' { store lexbuf; comment lexbuf } | [^'*' '\n' '\r']+ { store lexbuf; comment lexbuf } and string = parse | eof { raise Exit } | '\n' { newline lexbuf; store lexbuf; string lexbuf } | "\\\"" { add "\""; string lexbuf } | "\\\\" { add "\\"; string lexbuf } | "\\n" { add "\n"; string lexbuf } | "\\t" { add "\t"; string lexbuf } | "\\r" { add "\r"; string lexbuf } | '\\' ['0'-'9'] ['0'-'9'] ['0'-'9'] { let i = int_of_string (String.sub (lexeme lexbuf) 1 3) in if i >= 256 then error (Invalid_escaped_character i) (lexeme_start lexbuf); add (String.make 1 (char_of_int i)); string lexbuf } | '\\' { error Invalid_escape (lexeme_start lexbuf) } | '"' { lexeme_end lexbuf } | [^'"' '\\' '\n']+ { store lexbuf; string lexbuf } neko-2-4-0/libs/ocaml/neko/main.ml000066400000000000000000000056541464615675700167440ustar00rootroot00000000000000(* * Neko Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Printf type p_style = | StyleJava | StyleMSVC let print_style = ref StyleJava let normalize_path p = let l = String.length p in if l = 0 then "./" else match p.[l-1] with | '\\' | '/' -> p | _ -> p ^ "/" let report inf = let pos file line = (match !print_style with | StyleJava -> sprintf "%s:%d:" file line | StyleMSVC -> sprintf "%s(%d):" file line ) in prerr_endline (sprintf "%s : %s %s" (inf.Plugin.exn_pos pos) inf.Plugin.exn_name inf.Plugin.exn_message); exit 1 let dump file ch out = let data = (try Bytecode.read ch with Bytecode.Invalid_file -> IO.close_in ch; failwith ("Invalid bytecode file " ^ file)) in IO.close_in ch; Bytecode.dump out data; IO.close_out out let dump_exn = function | e -> raise e let compile file ch out = let ast = Parser.parse (Lexing.from_function (fun s p -> try IO.input ch s 0 p with IO.No_more_input -> 0)) file in IO.close_in ch; let data = Compile.compile file ast in Bytecode.write out data; IO.close_out out let compile_exn = function | Lexer.Error (m,p) -> Plugin.exn_infos "syntax error" (Lexer.error_msg m) (fun f -> Lexer.get_error_pos f p) | Parser.Error (m,p) -> Plugin.exn_infos "parse error" (Parser.error_msg m) (fun f -> Lexer.get_error_pos f p) | Compile.Error (m,p) -> Plugin.exn_infos "compile error" (Compile.error_msg m) (fun f -> Lexer.get_error_pos f p) | e -> raise e let main() = try let usage = "Neko v0.4 - (c)2005-2022 Haxe Foundation\n Usage : neko.exe [options] \n Options :" in let output = ref "n" in let args_spec = [ ("-msvc",Arg.Unit (fun () -> print_style := StyleMSVC),": use MSVC style errors"); ("-p", Arg.String (fun p -> Plugin.add_path p)," : add the file to path"); ("-o", Arg.String (fun ext -> output := String.lowercase ext)," : specify output extension"); ("-v", Arg.Unit (fun () -> Plugin.verbose := true),": verbose mode"); ] in Arg.parse args_spec (fun file -> Plugin.generate file !output ) usage; with | Plugin.Error inf -> report inf | Failure msg -> prerr_endline msg; exit 1 ;; Plugin.register "neko" "n" compile compile_exn; Plugin.register "n" "dump" dump dump_exn; at_exit mainneko-2-4-0/libs/ocaml/neko/neko.vcproj000066400000000000000000000045051464615675700176410ustar00rootroot00000000000000 neko-2-4-0/libs/ocaml/neko/parser.ml000066400000000000000000000146171464615675700173130ustar00rootroot00000000000000(* * Neko Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Ast type error_msg = | Unexpected of token | Unclosed of string | Duplicate_default | Unknown_macro of string | Invalid_macro_parameters of string * int exception Error of error_msg * pos let error_msg = function | Unexpected t -> "Unexpected "^(s_token t) | Unclosed s -> "Unclosed " ^ s | Duplicate_default -> "Duplicate default declaration" | Unknown_macro m -> "Unknown macro " ^ m | Invalid_macro_parameters (m,n) -> "Invalid number of parameters for macro " ^ m ^ " : " ^ string_of_int n ^ " required" let error m p = raise (Error (m,p)) let priority = function | "=" | "+=" | "-=" | "*=" | "/=" | "|=" | "&=" | "^=" -> -3 | "&&" | "||" -> -2 | "==" | "!=" | ">" | "<" | "<=" | ">=" -> -1 | "+" | "-" -> 0 | "*" | "/" -> 1 | "|" | "&" | "^" -> 2 | "<<" | ">>" | "%" | ">>>" -> 3 | _ -> 4 let rec make_binop op e ((v,p2) as e2) = match v with | EBinop (_op,_e,_e2) when priority _op <= priority op -> let _e = make_binop op e _e in EBinop (_op,_e,_e2) , punion (pos _e) (pos _e2) | _ -> EBinop (op,e,e2) , punion (pos e) (pos e2) let rec program = parser | [< e = expr; p = program >] -> e :: p | [< '(Semicolon,_); p = program >] -> p | [< '(Eof,_) >] -> [] and expr = parser | [< '(Const ((Ident k) as i),p); s >] -> (match s with parser | [< '(Binop ":",p2); >] -> ELabel k , punion p p2 | [< s >] -> expr_next (EConst i,p) s) | [< '(Const c,p); s >] -> expr_next (EConst c,p) s | [< '(BraceOpen,p1); e = block1; s >] -> (match s with parser | [< '(BraceClose,p2); s >] -> expr_next (e,punion p1 p2) s | [< >] -> error (Unclosed "{") p1) | [< '(ParentOpen,p1); e = expr; s >] -> (match s with parser | [< '(ParentClose,p2); s >] -> expr_next (EParenthesis e,punion p1 p2) s | [< >] -> error (Unclosed "(") p1) | [< '(Keyword Var,p1); v, p2 = variables p1; s >] -> expr_next (EVars v,punion p1 p2) s | [< '(Keyword While,p1); cond = expr; e = expr; s >] -> expr_next (EWhile (cond,e,NormalWhile), punion p1 (pos e)) s | [< '(Keyword Do,p1); e = expr; '(Keyword While,_); cond = expr; s >] -> expr_next (EWhile (cond,e,DoWhile), punion p1 (pos cond)) s | [< '(Keyword If,p1); cond = expr; e = expr; s >] -> let rec loop s = match s with parser | [< '(Keyword Else,_); e2 = expr; s >] -> expr_next (EIf (cond,e,Some e2),punion p1 (pos e2)) s | [< '(Semicolon,_); s >] -> loop s | [< >] -> expr_next (EIf (cond,e,None),punion p1 (pos e)) s in loop s | [< '(Keyword Function,p1); '(ParentOpen,po); p = parameter_names; s >] -> (match s with parser | [< '(ParentClose,_); e = expr; s >] -> expr_next (EFunction (p,e),punion p1 (pos e)) s | [< >] -> error (Unclosed "(") po) | [< '(Keyword Return,p1); s >] -> (match s with parser | [< e = expr; s >] -> expr_next (EReturn (Some e), punion p1 (pos e)) s | [< '(Semicolon,_); s >] -> expr_next (EReturn None,p1) s) | [< '(Keyword Break,p1); s >] -> (match s with parser | [< e = expr; s >] -> expr_next (EBreak (Some e), punion p1 (pos e)) s | [< '(Semicolon,_); s >] -> expr_next (EBreak None,p1) s) | [< '(Keyword Continue,p1); s >] -> expr_next (EContinue,p1) s | [< '(Keyword Try,p1); e = expr; '(Keyword Catch,_); '(Const (Ident name),_); e2 = expr; s >] -> expr_next (ETry (e,name,e2),punion p1 (pos e2)) s and expr_next e = parser | [< '(Dot,_); '(Const (Ident name),p); s >] -> expr_next (EField (e,name),punion (pos e) p) s | [< '(ParentOpen,po); pl = parameters; s >] -> (match s with parser | [< '(ParentClose,p); s >] -> expr_next (ECall (e,pl),punion (pos e) p) s | [< >] -> error (Unclosed "(") po) | [< '(BracketOpen,po); e2 = expr; s >] -> (match s with parser | [< '(BracketClose,p); s >] -> expr_next (EArray (e,e2),punion (pos e) p) s | [< >] -> error (Unclosed "[") po) | [< '(Binop op,_); e2 = expr; s >] -> make_binop op e e2 | [< >] -> e and block1 = parser | [< '(Const (Ident name),p); s >] -> (match s with parser | [< '(Arrow,_); e = expr; l = object_fields >] -> EObject ((name,e) :: l) | [< '(Binop ":",p2); b = block >] -> EBlock ( (ELabel name, punion p p2) :: b ) | [< e = expr_next (EConst (Ident name),p); b = block >] -> EBlock (e :: b)) | [< b = block >] -> EBlock b and block = parser | [< e = expr; b = block >] -> e :: b | [< '(Semicolon,_); b = block >] -> b | [< >] -> [] and object_fields = parser | [< '(Const (Ident name),_); '(Arrow,_); e = expr; l = object_fields >] -> (name,e) :: l | [< '(Comma,_); l = object_fields >] -> l | [< >] -> [] and parameter_names = parser | [< '(Const (Ident name),_); p = parameter_names >] -> name :: p | [< '(Comma,_); p = parameter_names >] -> p | [< >] -> [] and parameters = parser | [< e = expr; p = parameters_next >] -> e :: p | [< >] -> [] and parameters_next = parser | [< '(Comma,_); p = parameters >] -> p | [< >] -> [] and variables sp = parser | [< '(Const (Ident name),p); s >] -> (match s with parser | [< '(Binop "=",_); e = expr; v , p = variables_next (pos e) >] -> (name, Some e) :: v , p | [< v , p = variables_next p >] -> (name, None) :: v , p) and variables_next sp = parser | [< '(Comma,p); v = variables p >] -> v | [< >] -> [] , sp let parse code file = let old = Lexer.save() in Lexer.init file; let last = ref (Eof,null_pos) in let rec next_token x = let t, p = Lexer.token code in match t with | Comment s | CommentLine s -> next_token x | _ -> last := (t , p); Some (t , p) in try let l = program (Stream.from next_token) in Lexer.restore old; EBlock l, { pmin = 0; pmax = (pos !last).pmax; pfile = file } with | Stream.Error _ | Stream.Failure -> Lexer.restore old; error (Unexpected (fst !last)) (pos !last) | e -> Lexer.restore old; raise e neko-2-4-0/libs/ocaml/neko/plugin.ml000066400000000000000000000071331464615675700173100ustar00rootroot00000000000000(* * Neko Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) type exn_infos = { exn_name : string; exn_message : string; exn_pos : (string -> int -> string) -> string; } type filter = { ext_in : string; ext_out : string; transform : string -> IO.input -> unit IO.output -> unit; exceptions : exn -> exn_infos; } exception Error of exn_infos let paths = ref [""] let plugins = ref [] let verbose = ref false let register source dest trans exc = plugins := { ext_in = String.lowercase source; ext_out = String.lowercase dest; transform = trans; exceptions = exc; } :: !plugins let exn_infos name msg pos = { exn_name = name; exn_pos = pos; exn_message = msg; } let open_file ?(bin=false) f = let rec loop = function | [] -> None | path :: l -> let file = path ^ f in try let ch = (if bin then open_in_bin else open_in) file in Some (file, IO.input_channel ch) with _ -> loop l in loop (!paths) let switch_ext file ext = try Filename.chop_extension file ^ ext with _ -> file ^ ext let add_path path = let l = String.length path in if l > 0 && path.[l-1] != '\\' && path.[l-1] != '/' then paths := (path ^ "/") :: !paths else paths := path :: !paths let generate_loop file ch fext ext = if ext = fext then () else let rec loop fext acc = function | [] -> raise Not_found | x :: l when x.ext_in = fext && not (List.exists (fun p -> p.ext_in = x.ext_out) acc) -> if x.ext_out = ext then x :: acc else (try let l1 = loop x.ext_out (x :: acc) (!plugins) in (try let l2 = loop fext acc l in if List.length l2 < List.length l1 then l2 else l1 with Not_found -> l1) with Not_found -> loop fext acc l) | x :: l -> loop fext acc l in let ftarget = switch_ext file ("." ^ ext) in let genlist = (try List.rev (loop fext [] (!plugins)) with Not_found -> let fbase = Filename.basename file in failwith ("Don't know how to generate " ^ Filename.basename ftarget ^ " from " ^ fbase) ) in let execute x file ch out = try x.transform file ch out with e -> raise (Error (x.exceptions e)) in let rec loop file ch = function | [] -> assert false | [x] -> let out = IO.output_channel (open_out_bin ftarget) in execute x file ch out; (try IO.close_in ch with IO.Input_closed -> ()); (try IO.close_out out with IO.Output_closed -> ()); | x :: l -> let chin , out = IO.pipe() in execute x file ch out; (try IO.close_in ch with IO.Input_closed -> ()); (try IO.close_out out with IO.Output_closed -> ()); loop ("." ^ x.ext_out) chin l in loop file ch genlist let generate file ext = let fext = (try let p = String.rindex file '.' in String.sub file (p + 1) (String.length file - (p + 1)) with Not_found -> file ) in match open_file ~bin:true file with | None -> failwith ("File not found : " ^ file) | Some (file,ch) -> generate_loop file ch fext ext neko-2-4-0/libs/ocaml/neko/printer.ml000066400000000000000000000113201464615675700174660ustar00rootroot00000000000000(* * Neko Compiler * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Ast type 'a ctx = { ch : 'a IO.output; mutable level : int; mutable tabs : bool; } let create ch = { ch = ch; level = 0; tabs = true; } let newline ctx = IO.write ctx.ch '\n'; ctx.tabs <- false let level ctx b = ctx.level <- ctx.level + (if b then 1 else -1); newline ctx let print ctx = if not ctx.tabs then begin IO.nwrite ctx.ch (String.make (ctx.level * 4) ' '); ctx.tabs <- true; end; IO.printf ctx.ch let rec print_list ctx sep f = function | [] -> () | x :: [] -> f x | x :: l -> f x; print ctx "%s" sep; print_list ctx sep f l let rec print_ast ?(binop=false) ctx (e,p) = match e with | EConst c -> print ctx "%s" (s_constant c) | EBlock el -> print ctx "{"; level ctx true; List.iter (fun e -> print_ast ctx e; if ctx.tabs then begin print ctx ";"; newline ctx; end ) el; ctx.level <- ctx.level - 1; print ctx "}"; newline ctx; | EParenthesis e when not ctx.tabs -> print ctx "{ "; print_ast ctx e; print ctx " }"; | EParenthesis e -> print ctx "( "; print_ast ctx e; print ctx " )"; | EField (e,s) -> print_ast ctx e; print ctx ".%s" s; | ECall (e,el) -> print_ast ctx e; print ctx "("; print_list ctx "," (print_ast ctx) el; print ctx ")"; | EArray (e1,e2) -> print_ast ctx e1; print ctx "["; print_ast ctx e2; print ctx "]" | EVars vl -> print ctx "var "; print_list ctx ", " (fun (n,v) -> print ctx "%s" n; match v with | None -> () | Some e -> print ctx " = "; print_ast ctx e ) vl; print ctx ";"; newline ctx | EWhile (cond,e,NormalWhile) -> print ctx "while "; print_ast ctx cond; level_expr ctx e; | EWhile (cond,e,DoWhile) -> print ctx "do "; level_expr ctx e; print ctx "while "; print_ast ctx cond; newline ctx | EIf (cond,e,e2) -> print ctx "if "; print_ast ctx cond; level_expr ~closed:(e2=None) ctx e; (match e2 with | None -> () | Some e -> print ctx "else"; level_expr ctx e) | ETry (e,id,e2) -> print ctx "try"; level_expr ctx e; print ctx "catch %s" id; level_expr ctx e2; | EFunction (params,e) -> print ctx "function("; print_list ctx "," (print ctx "%s") params; print ctx ")"; level_expr ctx e; | EBinop (op,e1,e2) -> let tabs = ctx.tabs in if binop then (if tabs then print ctx "(" else print ctx "{"); print_ast ~binop:true ctx e1; print ctx " %s " op; print_ast ~binop:true ctx e2; if binop then (if tabs then print ctx ")" else print ctx "}"); | EReturn None -> print ctx "return;"; | EReturn (Some e) -> print ctx "return "; print_ast ctx e; | EBreak None -> print ctx "break;"; | EBreak (Some e) -> print ctx "break "; print_ast ctx e; | EContinue -> print ctx "continue" | ENext (e1,e2) -> print_ast ctx e1; print ctx ";"; newline ctx; print_ast ctx e2 | EObject [] -> print ctx "$new(null)" | EObject fl -> print ctx "{"; level ctx true; let rec loop = function | [] -> assert false | [f,e] -> print ctx "%s => " f; print_ast ctx e; newline ctx; | (f,e) :: l -> print ctx "%s => " f; print_ast ctx e; print ctx ", "; newline ctx; loop l in loop fl; level ctx false; print ctx "}" | ELabel s -> print ctx "%s:" s and level_expr ?(closed=false) ctx (e,p) = match e with | EBlock _ -> if ctx.tabs then print ctx " "; print_ast ctx (e,p) | ENext _ -> if ctx.tabs then print ctx " "; print_ast ctx (EBlock [(e,p)],p) | EParenthesis e -> if ctx.tabs then print ctx " "; print ctx "{"; level ctx true; print_ast ctx e; level ctx false; print ctx "}"; | _ -> level ctx true; print_ast ctx (e,p); if closed then print ctx ";"; level ctx false let print ctx ast = match fst ast with | EBlock el -> List.iter (fun e -> print_ast ctx e; if ctx.tabs then begin print ctx ";"; newline ctx; end ) el; | _ -> print_ast ctx ast let to_string ast = let ch = IO.output_string() in let ctx = create ch in print ctx ast; IO.close_out chneko-2-4-0/libs/ocaml/nxml.ml000066400000000000000000000112311464615675700160260ustar00rootroot00000000000000(* * Neko NXML for OCaml * Copyright (c)2005-2022 Haxe Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) open Nast type xml = | Node of string * (string * string) list * xml list | CData of string let node name att children = Node(name,att,children) let rec to_xml_rec p2 ast = let e , p = ast in let name = ref "" in let aval = ref None in let children = ref [] in (match e with | EConst c -> (match c with | True | False | Null | This | Builtin _ | Ident _ -> name := "v"; aval := Some (s_constant c) | Int i -> name := "i"; aval := Some (string_of_int i); | Float s -> name := "f"; aval := Some s; | String s -> name := "s"; aval := Some s; ) | EBlock el -> name := "b"; children := List.map (to_xml_rec p) el; | EParenthesis e -> name := "p"; children := [to_xml_rec p e]; | EField (e,f) -> name := "g"; aval := Some f; children := [to_xml_rec p e]; | ECall (e,el) -> name := "c"; children := to_xml_rec p e :: List.map (to_xml_rec p) el; | EArray (a,b) -> name := "a"; children := [to_xml_rec p a; to_xml_rec p b]; | EVars vl -> name := "var"; children := List.map (fun(v,e) -> node "v" [("v",v)] (match e with None -> [] | Some e -> [to_xml_rec p e]) ) vl; | EWhile (econd,e,NormalWhile) -> name := "while"; children := [to_xml_rec p econd; to_xml_rec p e]; | EWhile (econd,e,DoWhile) -> name := "do"; children := [to_xml_rec p e; to_xml_rec p econd]; | EIf (cond,e,eelse) -> name := "if"; children := to_xml_rec p cond :: to_xml_rec p e :: (match eelse with None -> [] | Some e -> [to_xml_rec p e]) | ETry (e1,v,e2) -> name := "try"; aval := Some v; children := [to_xml_rec p e1; to_xml_rec p e2]; | EFunction (args,e) -> name := "function"; aval := Some (String.concat ":" args); children := [to_xml_rec p e]; | EBinop (op,e1,e2) -> name := "o"; aval := Some op; children := [to_xml_rec p e1; to_xml_rec p e2]; | EReturn e -> name := "return"; children := (match e with None -> [] | Some e -> [to_xml_rec p e]); | EBreak e -> name := "break"; children := (match e with None -> [] | Some e -> [to_xml_rec p e]); | EContinue -> name := "continue"; | ENext (e1,e2) -> name := "next"; children := [to_xml_rec p e1; to_xml_rec p e2]; | EObject fl -> name := "object"; children := List.map (fun(v,e) -> node "v" [("v",v)] [to_xml_rec p e]) fl; | ELabel v -> name := "label"; aval := Some v; | ESwitch (e,cases,def) -> name := "switch"; let cases = List.map (fun(e1,e2) -> node "case" [] [to_xml_rec p e1; to_xml_rec p e2]) cases in children := to_xml_rec p e :: (match def with None -> cases | Some e -> node "default" [] [to_xml_rec p e] :: cases ); | ENeko s -> name := "neko"; children := [CData s]; ); let pos = (if p.psource <> p2.psource then [("p",p.psource ^ ":" ^ string_of_int p.pline)] else if p.pline <> p2.pline then [("p",string_of_int p.pline)] else [] ) in let aval = (match !aval with None -> [] | Some v -> [("v",v)]) in node !name (List.append pos aval) !children let to_xml ast = to_xml_rec null_pos ast let rec write_fmt_rec tabs ch x = match x with | CData s -> IO.printf ch "%s" tabs s | Node (name,att,children) -> IO.printf ch "%s<%s%s" tabs name (String.concat "" (List.map (fun(a,v) -> " " ^ a ^ "=\"" ^ escape v ^ "\"") att)); match children with | [] -> IO.nwrite ch "/>" | l -> IO.nwrite ch ">\n"; List.iter (fun(x) -> write_fmt_rec (tabs ^ " ") ch x; IO.write ch '\n') l; IO.printf ch "%s" tabs name let write_fmt ch x = write_fmt_rec "" ch (node "nxml" [] [x]) let rec write_rec ch x = match x with | CData s -> IO.printf ch "" s | Node (name,att,children) -> IO.printf ch "<%s%s" name (String.concat "" (List.map (fun(a,v) -> " " ^ a ^ "=\"" ^ escape v ^ "\"") att)); match children with | [] -> IO.nwrite ch "/>" | l -> IO.nwrite ch ">"; List.iter (fun(x) -> write_rec ch x) l; IO.printf ch "" name let write ch x = write_rec ch (node "nxml" [] [x]) neko-2-4-0/libs/regexp/000077500000000000000000000000001464615675700147175ustar00rootroot00000000000000neko-2-4-0/libs/regexp/CMakeLists.txt000066400000000000000000000044521464615675700174640ustar00rootroot00000000000000 ###################### # regexp.ndll add_library(regexp.ndll MODULE regexp.c) if (STATIC_PCRE2) set_target_properties(regexp.ndll PROPERTIES COMPILE_DEFINITIONS PCRE2_STATIC_LINK ) set(PCRE2_CONFIG URL https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.42/pcre2-10.42.tar.gz URL_HASH SHA256=c33b418e3b936ee3153de2c61cc638e7e4fe3156022a5c77d0711bcbb9d64f1f ) if (WIN32) ExternalProject_Add(pcre2 ${EP_CONFIGS} ${PCRE2_CONFIG} CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/libs/src/install-prefix -Wno-dev -DPCRE2_BUILD_PCRE2GREP=OFF -DPCRE2_BUILD_TESTS=OFF -DPCRE2_SUPPORT_JIT=ON -DPCRE2_SUPPORT_UNICODE=ON -DBUILD_SHARED_LIBS=OFF ) set(PCRE2_LIBRARIES optimized ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/pcre2-8-static.lib debug ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/pcre2-8-staticd.lib ) else() if (APPLE) set(PCRE2_CFLAGS "-w -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") else() set(PCRE2_CFLAGS "-w") endif() set(PCRE2_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/install-prefix/lib/libpcre2-8.a ) ExternalProject_Add(pcre2 ${EP_CONFIGS} ${PCRE2_CONFIG} CONFIGURE_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/pcre2 && autoreconf -f -i && ./configure --prefix=${CMAKE_BINARY_DIR}/libs/src/install-prefix --with-pic --enable-silent-rules --enable-jit --enable-shared=no --enable-static=yes --silent BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/pcre2 && make "CFLAGS=${PCRE2_CFLAGS}" INSTALL_COMMAND cd ${CMAKE_BINARY_DIR}/libs/src/pcre2 && make install BYPRODUCTS ${PCRE2_LIBRARIES} ) endif() set_target_properties(pcre2 PROPERTIES ${EP_PROPS}) set(PCRE2_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/libs/src/install-prefix/include) add_dependencies(regexp.ndll pcre2) # Download project for fat source archive add_dependencies(download_deps pcre2-download) else() find_package(PCRE2 REQUIRED) endif() target_include_directories(regexp.ndll PRIVATE ${PCRE2_INCLUDE_DIRS}) target_link_libraries(regexp.ndll libneko ${PCRE2_LIBRARIES}) set_target_properties(regexp.ndll PROPERTIES PREFIX "" OUTPUT_NAME regexp SUFFIX .ndll ) install ( TARGETS regexp.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/regexp/regexp.c000066400000000000000000000214611464615675700163610ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #define PCRE2_CODE_UNIT_WIDTH 8 #ifdef PCRE2_STATIC_LINK // this must be defined before loading the pcre header #define PCRE2_STATIC #endif #include #define PCRE(o) ((pcredata*)val_data(o)) typedef struct { // The compiled regex code pcre2_code *regex; // Number of capture groups int n_groups; // The last string matched value str; // Pointer to the allocated memory for match data pcre2_match_data *match_data; } pcredata; DEFINE_KIND(k_regexp); static field id_pos; static field id_len; /**

Regexp

Regular expressions using PCRE engine.

**/ static void free_regexp( value p ) { pcre2_code_free( PCRE(p)->regex ); pcre2_match_data_free( PCRE(p)->match_data ); } static int do_match( pcredata *d, const char *str, int len, int pos ) { int res = pcre2_match(d->regex,str,len,pos,0,d->match_data,NULL); if( res >= 0 ) return 1; d->str = val_null; // empty string prevents trying to access the data after a failed match if( res != PCRE2_ERROR_NOMATCH ) val_throw(alloc_string("An error occurred while running pcre2_match")); return 0; } /** regexp_new_options : reg:string -> options:string -> 'regexp Build a new regexpr with the following options :
  • i : case insensitive matching
  • s : . match anything including newlines
  • m : treat the input as a multiline string
  • u : run in utf8 mode
  • g : turn off greedy behavior
**/ static value regexp_new_options( value s, value opt ) { val_check(s,string); val_check(opt,string); { value v; int error_num; size_t err_offset; pcre2_code *p; pcredata *pdata; char *o = val_string(opt); int options = 0; while( *o ) { switch( *o++ ) { case 'i': options |= PCRE2_CASELESS; break; case 's': options |= PCRE2_DOTALL; break; case 'm': options |= PCRE2_MULTILINE; break; case 'u': options |= PCRE2_UTF; break; case 'g': options |= PCRE2_UNGREEDY; break; default: neko_error(); break; } } p = pcre2_compile(val_string(s),val_strlen(s),options,&error_num,&err_offset,NULL); if( p == NULL ) { buffer b = alloc_buffer("Regexp compilation error : "); PCRE2_UCHAR error_buffer[256]; pcre2_get_error_message(error_num,error_buffer,sizeof(error_buffer)); buffer_append(b,error_buffer); buffer_append(b," in "); val_buffer(b,s); bfailure(b); } v = alloc_abstract(k_regexp,alloc(sizeof(pcredata))); pdata = PCRE(v); pdata->regex = p; pdata->str = val_null; pdata->n_groups = 0; pcre2_pattern_info(pdata->regex,PCRE2_INFO_CAPTURECOUNT,&pdata->n_groups); pdata->n_groups++; pdata->match_data = pcre2_match_data_create_from_pattern(pdata->regex,NULL); val_gc(v,free_regexp); return v; } } /** regexp_new : string -> 'regexp Build a new regexp **/ static value regexp_new( value s ) { return regexp_new_options(s,alloc_string("")); } /** regexp_match : 'regexp -> string -> pos:int -> len:int -> bool Match [len] chars of a string starting at [pos] using the regexp. Return true if match found **/ static value regexp_match( value o, value s, value p, value len ) { pcredata *d; int pp,ll; val_check_kind(o,k_regexp); val_check(s,string); val_check(p,int); val_check(len,int); pp = val_int(p); ll = val_int(len); if( pp < 0 || ll < 0 || pp > val_strlen(s) || pp + ll > val_strlen(s) ) neko_error(); d = PCRE(o); if( do_match(d,val_string(s),ll+pp,pp) ) { d->str = s; return val_true; } else { d->str = val_null; return val_false; } } static value do_replace( value o, value s, value s2, bool all ) { val_check_kind(o,k_regexp); val_check(s,string); val_check(s2,string); { pcredata *d = PCRE(o); buffer b = alloc_buffer(NULL); const char *str = val_string(s); const char *str2 = val_string(s2); int len = val_strlen(s); int len2 = val_strlen(s2); int pos = 0; size_t *matches; // TODO: Consider pcre2_substitute() while( do_match(d,str,len,pos) ) { matches = pcre2_get_ovector_pointer(d->match_data); buffer_append_sub(b,str+pos,matches[0] - pos); buffer_append_sub(b,str2,len2); pos = matches[1]; if( !all ) break; } d->str = val_null; buffer_append_sub(b,str+pos,len-pos); return buffer_to_string(b); } } /** regexp_replace : 'regexp -> from:string -> by:string -> string Perform a replacement using a regexp **/ static value regexp_replace( value o, value s, value s2 ) { return do_replace(o,s,s2,false); } /** regexp_replace_all : 'regexp -> from:string -> by:string -> string Perform a replacement of all matched substrings using a regexp **/ static value regexp_replace_all( value o, value s, value s2 ) { return do_replace(o,s,s2,true); } /** regexp_replace_fun : 'regexp -> from:string -> f:('regexp -> any) -> string Perform a replacement of all matched substrings by calling [f] for every match **/ static value regexp_replace_fun( value o, value s, value f ) { val_check_kind(o,k_regexp); val_check(s,string); val_check_function(f,1); { pcredata *d = PCRE(o); buffer b = alloc_buffer(NULL); int pos = 0; int len = val_strlen(s); const char *str = val_string(s); size_t *matches; d->str = s; while( do_match(d,str,len,pos) ) { matches = pcre2_get_ovector_pointer(d->match_data); buffer_append_sub(b,str+pos,matches[0] - pos); val_buffer(b,val_call1(f,o)); pos = matches[1]; } d->str = val_null; buffer_append_sub(b,str+pos,len-pos); return buffer_to_string(b); } } /** regexp_matched : 'regexp -> n:int -> string? Return the [n]th matched block by the regexp. If [n] is 0 then return the whole matched substring. If the [n]th matched block was optional and not matched, returns null **/ static value regexp_matched( value o, value n ) { pcredata *d; int m; val_check_kind(o,k_regexp); d = PCRE(o); val_check(n,int); m = val_int(n); if( m < 0 || m >= d->n_groups || val_is_null(d->str) ) neko_error(); { size_t *matches = pcre2_get_ovector_pointer(d->match_data); int start = matches[m*2]; int len = matches[m*2+1] - start; value str; if( start == -1 ) return val_null; str = alloc_empty_string(len); memcpy((char*)val_string(str),val_string(d->str)+start,len); return str; } } /** regexp_matched_pos : 'regexp -> n:int -> { pos => int, len => int } Return the [n]th matched block position by the regexp. If [n] is 0 then return the whole matched substring position **/ static value regexp_matched_pos( value o, value n ) { pcredata *d; int m; val_check_kind(o,k_regexp); d = PCRE(o); val_check(n,int); m = val_int(n); if( m < 0 || m >= d->n_groups || val_is_null(d->str) ) neko_error(); { size_t *matches = pcre2_get_ovector_pointer(d->match_data); int start = matches[m*2]; int len = matches[m*2+1] - start; value o = alloc_object(NULL); alloc_field(o,id_pos,alloc_int(start)); alloc_field(o,id_len,alloc_int(len)); return o; } } /** regexp_matched_num : 'regexp -> int Return the total number of matched groups, or -1 if the regexp has not been matched yet **/ static value regexp_matched_num( value o ) { pcredata *d; val_check_kind(o,k_regexp); d = PCRE(o); if( val_is_null(d->str) ) return alloc_int(-1); return alloc_int(d->n_groups); } void regexp_main() { id_pos = val_id("pos"); id_len = val_id("len"); } DEFINE_PRIM(regexp_new,1); DEFINE_PRIM(regexp_new_options,2); DEFINE_PRIM(regexp_match,4); DEFINE_PRIM(regexp_replace,3); DEFINE_PRIM(regexp_replace_all,3); DEFINE_PRIM(regexp_replace_fun,3); DEFINE_PRIM(regexp_matched,2); DEFINE_PRIM(regexp_matched_pos,2); DEFINE_PRIM(regexp_matched_num,1); DEFINE_ENTRY_POINT(regexp_main); /* ************************************************************************ */ neko-2-4-0/libs/sqlite/000077500000000000000000000000001464615675700147265ustar00rootroot00000000000000neko-2-4-0/libs/sqlite/CMakeLists.txt000066400000000000000000000024301464615675700174650ustar00rootroot00000000000000 ###################### # sqlite.ndll if (STATIC_SQLITE3) ExternalProject_Add(SQLite3 ${EP_CONFIGS} URL https://www.sqlite.org/2022/sqlite-autoconf-3400100.tar.gz URL_HASH SHA256=2c5dea207fa508d765af1ef620b637dcb06572afa6f01f0815bd5bbf864b33d9 CONFIGURE_COMMAND echo skip config BUILD_COMMAND echo skip build INSTALL_COMMAND echo skip install ) add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/libs/src/SQLite3/sqlite3.c DEPENDS SQLite3 ) add_library(sqlite.ndll MODULE sqlite.c ${CMAKE_BINARY_DIR}/libs/src/SQLite3/sqlite3.c ) target_include_directories(sqlite.ndll PRIVATE ${CMAKE_BINARY_DIR}/libs/src/SQLite3) target_link_libraries(sqlite.ndll libneko) target_compile_definitions(sqlite.ndll PRIVATE SQLITE_MAX_VARIABLE_NUMBER=250000 SQLITE_ENABLE_RTREE=1) # Download project for fat source archive add_dependencies(download_deps SQLite3-download) else() add_library(sqlite.ndll MODULE sqlite.c) find_package(SQLite3 REQUIRED) target_include_directories(sqlite.ndll PRIVATE ${SQLite3_INCLUDE_DIRS}) target_link_libraries(sqlite.ndll libneko ${SQLite3_LIBRARIES}) endif() set_target_properties(sqlite.ndll PROPERTIES PREFIX "" OUTPUT_NAME sqlite SUFFIX .ndll ) install ( TARGETS sqlite.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/sqlite/sqlite.c000066400000000000000000000214351464615675700164000ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include /**

SQLite

Sqlite is a small embeddable SQL database that store all its data into a single file. See https://sqlite.org for more details.

**/ DEFINE_KIND(k_db); DEFINE_KIND(k_result); #define val_db(v) ((database*)val_data(v)) #define val_result(v) ((result*)val_data(v)) typedef struct _database { sqlite3 *db; value last; } database; typedef struct _result { value database; int ncols; int count; field *names; int *bools; int done; int first; sqlite3_stmt *r; } result; static void sqlite_error( sqlite3 *db ) { buffer b = alloc_buffer("Sqlite error : "); buffer_append(b,sqlite3_errmsg(db)); val_throw(buffer_to_string(b)); } static void finalize_result( result *r, int exc ) { r->first = 0; r->done = 1; if( r->ncols == 0 ) r->count = sqlite3_changes(val_db(r->database)->db); if( sqlite3_finalize(r->r) != SQLITE_OK && exc ) val_throw(alloc_string("Could not finalize request")); r->r = NULL; val_db(r->database)->last = NULL; r->database = NULL; } static void free_db( value v ) { database *db = val_db(v); if( db->last != NULL ) finalize_result(val_result(db->last),0); if( sqlite3_close(db->db) != SQLITE_OK ) { // No exception : we shouldn't alloc memory in a finalizer anyway } } /** connect : filename:string -> 'db Open or create the database stored in the specified file. **/ static value connect( value filename ) { int err; database *db = (database*)alloc(sizeof(database)); value v; val_check(filename,string); db->last = NULL; if( (err = sqlite3_open(val_string(filename),&db->db)) != SQLITE_OK ) { buffer b = alloc_buffer("Sqlite error : "); buffer_append(b,sqlite3_errmsg(db->db)); sqlite3_close(db->db); val_throw(buffer_to_string(b)); } v = alloc_abstract(k_db,db); val_gc(v,free_db); return v; } /** close : 'db -> void Closes the database. **/ static value close( value v ) { val_check_kind(v,k_db); free_db(v); val_gc(v,NULL); val_kind(v) = NULL; return val_null; } /** last_insert_id : 'db -> int Returns the last inserted auto_increment id. **/ static value last_insert_id( value db ) { val_check_kind(db,k_db); return alloc_int(sqlite3_last_insert_rowid(val_db(db)->db)); } /** request : 'db -> sql:string -> 'result Executes the SQL request and returns its result **/ static value request( value v, value sql ) { database *db; result *r; const char *tl; int i,j; val_check_kind(v,k_db); val_check(sql,string); db = val_db(v); r = (result*)alloc(sizeof(result)); r->database = v; if( sqlite3_prepare(db->db,val_string(sql),val_strlen(sql),&r->r,&tl) != SQLITE_OK ) { buffer b = alloc_buffer("Sqlite error in "); val_buffer(b,sql); buffer_append(b," : "); buffer_append(b,sqlite3_errmsg(db->db)); val_throw(buffer_to_string(b)); } if( *tl ) { sqlite3_finalize(r->r); val_throw(alloc_string("Cannot execute several SQL requests at the same time")); } r->ncols = sqlite3_column_count(r->r); r->names = (field*)alloc_private(sizeof(field)*r->ncols); r->bools = (int*)alloc_private(sizeof(int)*r->ncols); r->first = 1; r->done = 0; for(i=0;incols;i++) { field id = val_id(sqlite3_column_name(r->r,i)); const char *dtype = sqlite3_column_decltype(r->r,i); for(j=0;jnames[j] == id ) { if( strcmp(sqlite3_column_name(r->r,i),sqlite3_column_name(r->r,j)) == 0 ) { buffer b = alloc_buffer("Error, same field is two times in the request "); val_buffer(b,sql); sqlite3_finalize(r->r); val_throw(buffer_to_string(b)); } else { buffer b = alloc_buffer("Error, same field ids for : "); buffer_append(b,sqlite3_column_name(r->r,i)); buffer_append(b," and "); buffer_append(b,sqlite3_column_name(r->r,j)); buffer_append_char(b,'.'); sqlite3_finalize(r->r); val_throw(buffer_to_string(b)); } } r->names[i] = id; r->bools[i] = dtype?(strcmp(dtype,"BOOL") == 0):0; } // changes in an update/delete if( db->last != NULL ) finalize_result(val_result(db->last),0); db->last = alloc_abstract(k_result,r); return db->last; } /** result_get_length : 'result -> int Returns the number of rows in the result or the number of rows changed by the request. **/ static value result_get_length( value v ) { result *r; val_check_kind(v,k_result); r = val_result(v); if( r->ncols != 0 ) neko_error(); // ??? return alloc_int(r->count); } /** result_get_nfields : 'result -> int Returns the number of fields in the result. **/ static value result_get_nfields( value r ) { val_check_kind(r,k_result); return alloc_int(val_result(r)->ncols); } /** result_next : 'result -> object? Returns the next row in the result or [null] if no more result. **/ static value result_next( value v ) { int i; result *r; val_check_kind(v,k_result); r = val_result(v); if( r->done ) return val_null; switch( sqlite3_step(r->r) ) { case SQLITE_ROW: r->first = 0; v = alloc_object(NULL); for(i=0;incols;i++) { value f; switch( sqlite3_column_type(r->r,i) ) { case SQLITE_NULL: f = val_null; break; case SQLITE_INTEGER: if( r->bools[i] ) f = alloc_bool(sqlite3_column_int(r->r,i)); else f = alloc_best_int(sqlite3_column_int(r->r,i)); break; case SQLITE_FLOAT: f = alloc_float(sqlite3_column_double(r->r,i)); break; case SQLITE_TEXT: f = alloc_string((char*)sqlite3_column_text(r->r,i)); break; case SQLITE_BLOB: { int size = sqlite3_column_bytes(r->r,i); f = alloc_empty_string(size); memcpy(val_string(f),sqlite3_column_blob(r->r,i),size); break; } default: { buffer b = alloc_buffer("Unknown Sqlite type #"); val_buffer(b,alloc_int(sqlite3_column_type(r->r,i))); val_throw(buffer_to_string(b)); } } alloc_field(v,r->names[i],f); } return v; case SQLITE_DONE: finalize_result(r,1); return val_null; case SQLITE_BUSY: val_throw(alloc_string("Database is busy")); case SQLITE_ERROR: sqlite_error(val_db(r->database)->db); default: neko_error(); } return val_null; } /** result_get : 'result -> n:int -> string Return the [n]th field of the current result row. **/ static value result_get( value v, value n ) { result *r; val_check_kind(v,k_result); r = val_result(v); if( val_int(n) < 0 || val_int(n) >= r->ncols ) neko_error(); if( r->first ) result_next(v); if( r->done ) neko_error(); return alloc_string((char*)sqlite3_column_text(r->r,val_int(n))); } /** result_get_int : 'result -> n:int -> int Return the [n]th field of the current result row as an integer. **/ static value result_get_int( value v, value n ) { result *r; val_check_kind(v,k_result); r = val_result(v); if( val_int(n) < 0 || val_int(n) >= r->ncols ) neko_error(); if( r->first ) result_next(v); if( r->done ) neko_error(); return alloc_best_int(sqlite3_column_int(r->r,val_int(n))); } /** result_get_float : 'result -> n:int -> float Return the [n]th field of the current result row as a float. **/ static value result_get_float( value v, value n ) { result *r; val_check_kind(v,k_result); r = val_result(v); if( val_int(n) < 0 || val_int(n) >= r->ncols ) neko_error(); if( r->first ) result_next(v); if( r->done ) neko_error(); return alloc_float(sqlite3_column_double(r->r,val_int(n))); } DEFINE_PRIM(connect,1); DEFINE_PRIM(close,1); DEFINE_PRIM(request,2); DEFINE_PRIM(last_insert_id,1); DEFINE_PRIM(result_get_length,1); DEFINE_PRIM(result_get_nfields,1); DEFINE_PRIM(result_next,1); DEFINE_PRIM(result_get,2); DEFINE_PRIM(result_get_int,2); DEFINE_PRIM(result_get_float,2); /* ************************************************************************ */ neko-2-4-0/libs/ssl/000077500000000000000000000000001464615675700142265ustar00rootroot00000000000000neko-2-4-0/libs/ssl/CMakeLists.txt000066400000000000000000000050121464615675700167640ustar00rootroot00000000000000 ###################### # ssl.ndll add_library(ssl.ndll MODULE ssl.c) if (STATIC_MBEDTLS) set(MBEDTLS_CMAKE_ARGS -Wno-dev -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON -DMBEDTLS_USER_CONFIG_FILE=${CMAKE_CURRENT_SOURCE_DIR}/mbedtls_config.h ) if (UNIX) list(APPEND MBEDTLS_CMAKE_ARGS -DLINK_WITH_PTHREAD=ON -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} ${ARG_PIC} ) endif() if (WIN32) set(MBEDTLS_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/MbedTLS-build/library/${CMAKE_CFG_INTDIR}/mbedx509.lib ${CMAKE_BINARY_DIR}/libs/src/MbedTLS-build/library/${CMAKE_CFG_INTDIR}/mbedtls.lib ${CMAKE_BINARY_DIR}/libs/src/MbedTLS-build/library/${CMAKE_CFG_INTDIR}/mbedcrypto.lib ) target_link_libraries(ssl.ndll ws2_32 Advapi32 Crypt32 bcrypt) else() set(MBEDTLS_LIBRARIES ${CMAKE_BINARY_DIR}/libs/src/MbedTLS-build/library/libmbedx509.a ${CMAKE_BINARY_DIR}/libs/src/MbedTLS-build/library/libmbedtls.a ${CMAKE_BINARY_DIR}/libs/src/MbedTLS-build/library/libmbedcrypto.a ) endif() ExternalProject_Add(MbedTLS ${EP_CONFIGS} URL https://github.com/Mbed-TLS/mbedtls/releases/download/v3.6.0/mbedtls-3.6.0.tar.bz2 URL_HASH SHA256=3ecf94fcfdaacafb757786a01b7538a61750ebd85c4b024f56ff8ba1490fcd38 CMAKE_ARGS ${MBEDTLS_CMAKE_ARGS} PATCH_COMMAND ${CMAKE_COMMAND} -Dsource=${CMAKE_SOURCE_DIR} -DMbedTLS_source=${CMAKE_BINARY_DIR}/libs/src/MbedTLS -P ${CMAKE_SOURCE_DIR}/cmake/patch_mbedtls.cmake INSTALL_COMMAND echo skip install BUILD_BYPRODUCTS ${MBEDTLS_LIBRARIES} ) set_target_properties(MbedTLS PROPERTIES ${EP_PROPS}) set(MBEDTLS_INCLUDE_DIR ${CMAKE_BINARY_DIR}/libs/src/MbedTLS/include) add_dependencies(ssl.ndll MbedTLS) # Download project for fat source archive add_dependencies(download_deps MbedTLS-download) else() find_package(MbedTLS REQUIRED) endif() target_include_directories(ssl.ndll PRIVATE ${MBEDTLS_INCLUDE_DIR} ) target_compile_definitions(ssl.ndll PRIVATE -DMBEDTLS_USER_CONFIG_FILE="${CMAKE_CURRENT_SOURCE_DIR}/mbedtls_config.h" ) if(APPLE) find_library(SECURITY_LIBRARY Security REQUIRED) find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED) target_link_libraries(ssl.ndll ${COREFOUNDATION_LIBRARY} ${SECURITY_LIBRARY}) endif() target_link_libraries(ssl.ndll libneko ${MBEDTLS_LIBRARIES}) set_target_properties(ssl.ndll PROPERTIES PREFIX "" OUTPUT_NAME ssl SUFFIX .ndll ) install ( TARGETS ssl.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/ssl/mbedtls_config.h000066400000000000000000000002101464615675700173470ustar00rootroot00000000000000#ifdef _WIN32 #define MBEDTLS_THREADING_ALT #endif #ifndef _WIN32 #define MBEDTLS_THREADING_PTHREAD #endif #define MBEDTLS_THREADING_C neko-2-4-0/libs/ssl/ssl.c000066400000000000000000000552601464615675700152030ustar00rootroot00000000000000 #define IMPLEMENT_API #define NEKO_COMPATIBLE #include #include #include #ifdef _MSC_VER #include #include #else #include #include typedef int SOCKET; #endif #ifdef NEKO_MAC #include #include #include #include #endif #define SOCKET_ERROR (-1) #define NRETRYS 20 #include "mbedtls/platform.h" #include "mbedtls/error.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/md.h" #include "mbedtls/pk.h" #include "mbedtls/oid.h" #include "mbedtls/x509_crt.h" #include "mbedtls/ssl.h" #ifdef MBEDTLS_PSA_CRYPTO_C #include #endif #define val_ssl(o) (mbedtls_ssl_context*)val_data(o) #define val_conf(o) (mbedtls_ssl_config*)val_data(o) #define val_cert(o) (mbedtls_x509_crt*)val_data(o) #define val_pkey(o) (mbedtls_pk_context*)val_data(o) DEFINE_KIND( k_ssl_conf ); DEFINE_KIND( k_ssl ); DEFINE_KIND( k_cert ); DEFINE_KIND( k_pkey ); static vkind k_socket; static mbedtls_entropy_context entropy; static mbedtls_ctr_drbg_context ctr_drbg; static void free_cert( value v ){ mbedtls_x509_crt *x = val_cert(v); mbedtls_x509_crt_free(x); } static void free_pkey( value v ){ mbedtls_pk_context *k = val_pkey(v); mbedtls_pk_free(k); } static value block_error() { #ifdef NEKO_WINDOWS int err = WSAGetLastError(); if( err == WSAEWOULDBLOCK || err == WSAEALREADY || err == WSAETIMEDOUT ) #else if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EALREADY ) #endif val_throw(alloc_string("Blocking")); val_throw(alloc_string("ssl network error")); return val_true; } static value ssl_error( int ret ){ char buf[256]; mbedtls_strerror(ret, buf, sizeof(buf)); val_throw(alloc_string(buf)); return val_false; } static value ssl_new( value config ) { int ret; mbedtls_ssl_context *ssl; val_check_kind(config,k_ssl_conf); ssl = (mbedtls_ssl_context *)alloc(sizeof(mbedtls_ssl_context)); mbedtls_ssl_init(ssl); if( (ret = mbedtls_ssl_setup(ssl, val_conf(config))) != 0 ){ mbedtls_ssl_free(ssl); return ssl_error(ret); } return alloc_abstract( k_ssl, ssl ); } static value ssl_close( value ssl ) { mbedtls_ssl_context *s; val_check_kind(ssl,k_ssl); s = val_ssl(ssl); mbedtls_ssl_free( s ); val_kind(ssl) = NULL; return val_true; } static value ssl_handshake( value ssl ) { int r; val_check_kind(ssl,k_ssl); POSIX_LABEL(handshake_again); r = mbedtls_ssl_handshake( val_ssl(ssl) ); if( r == SOCKET_ERROR ) { HANDLE_EINTR(handshake_again); return block_error(); }else if( r != 0 ) return ssl_error(r); return val_true; } int net_read( void *fd, unsigned char *buf, size_t len ){ return recv((SOCKET)(int_val)fd, (char *)buf, len, 0); } int net_write( void *fd, const unsigned char *buf, size_t len ){ return send((SOCKET)(int_val)fd, (char *)buf, len, 0); } static value ssl_set_socket( value ssl, value socket ) { val_check_kind(ssl,k_ssl); if( k_socket == NULL ) k_socket = kind_lookup("socket"); val_check_kind(socket,k_socket); mbedtls_ssl_set_bio( val_ssl(ssl), val_data(socket), net_write, net_read, NULL ); return val_true; } static value ssl_set_hostname( value ssl, value hostname ){ int ret; val_check_kind(ssl,k_ssl); val_check(hostname,string); if( (ret = mbedtls_ssl_set_hostname(val_ssl(ssl), val_string(hostname))) != 0 ) return ssl_error(ret); return val_true; } static value ssl_get_peer_certificate( value ssl ){ value v; const mbedtls_x509_crt *crt; val_check_kind(ssl,k_ssl); crt = mbedtls_ssl_get_peer_cert(val_ssl(ssl)); if( crt == NULL ) return val_null; v = alloc_abstract( k_cert, (void *)crt ); return v; } static value ssl_get_verify_result( value ssl ){ int r; val_check_kind(ssl,k_ssl); r = mbedtls_ssl_get_verify_result( val_ssl(ssl) ); if( r == 0 ) return val_true; else if( r != -1 ) return val_false; val_throw(alloc_string("not available")); return val_false; } static value ssl_send_char( value ssl, value v ) { unsigned char cc; int c; val_check_kind(ssl,k_ssl); val_check(v,int); c = val_int(v); if( c < 0 || c > 255 ) neko_error(); cc = (unsigned char) c; mbedtls_ssl_write( val_ssl(ssl), &cc, 1 ); return val_true; } static value ssl_send( value ssl, value data, value pos, value len ) { int p,l,dlen; val_check_kind(ssl,k_ssl); val_check(data,string); val_check(pos,int); val_check(len,int); p = val_int(pos); l = val_int(len); dlen = val_strlen(data); if( p < 0 || l < 0 || p > dlen || p + l > dlen ) neko_error(); POSIX_LABEL(send_again); dlen = mbedtls_ssl_write( val_ssl(ssl), (const unsigned char *)val_string(data) + p, l ); if( dlen == SOCKET_ERROR ) { HANDLE_EINTR(send_again); return block_error(); } return alloc_int(dlen); } static value ssl_write( value ssl, value data ) { int len, slen; const unsigned char *s; mbedtls_ssl_context *ctx; val_check_kind(ssl,k_ssl); val_check(data,string); s = (const unsigned char *)val_string( data ); len = val_strlen( data ); ctx = val_ssl(ssl); while( len > 0 ) { POSIX_LABEL( write_again ); slen = mbedtls_ssl_write( ctx, s, len ); if( slen == SOCKET_ERROR ) { HANDLE_EINTR( write_again ); return block_error(); } s += slen; len -= slen; } return val_true; } static value ssl_recv_char(value ssl) { unsigned char c; int r; val_check_kind(ssl,k_ssl); r = mbedtls_ssl_read( val_ssl(ssl), &c, 1 ); if( r <= 0 ) ssl_error(r); return alloc_int( c ); } static value ssl_recv( value ssl, value data, value pos, value len ) { int p,l,dlen; void * buf; val_check_kind(ssl,k_ssl); val_check(data,string); val_check(pos,int); val_check(len,int); p = val_int( pos ); l = val_int( len ); buf = (void *) (val_string(data) + p); POSIX_LABEL(recv_again); dlen = mbedtls_ssl_read( val_ssl(ssl), buf, l ); if( dlen == SOCKET_ERROR ) { HANDLE_EINTR(recv_again); return block_error(); } if (dlen == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY || dlen == MBEDTLS_ERR_SSL_WANT_READ #ifdef MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET || dlen == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET #endif ) return alloc_int(0); if( dlen < 0 ) ssl_error(dlen); return alloc_int( dlen ); } static value ssl_read( value ssl ) { int len, bufsize = 256; buffer b; unsigned char buf[256]; mbedtls_ssl_context *ctx; val_check_kind(ssl,k_ssl); ctx = val_ssl(ssl); b = alloc_buffer(NULL); while( true ) { POSIX_LABEL(read_again); len = mbedtls_ssl_read( ctx, buf, bufsize ); if( len == SOCKET_ERROR ) { HANDLE_EINTR(read_again); return block_error(); } if( len == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY || len == MBEDTLS_ERR_SSL_WANT_READ #ifdef MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET || len == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET #endif ) break; if( len == 0 ) break; buffer_append_sub(b,(const char *)buf,len); } return buffer_to_string(b); } static value conf_new( value server ) { int ret; mbedtls_ssl_config *conf; val_check(server,bool); conf = (mbedtls_ssl_config *)alloc(sizeof(mbedtls_ssl_config)); mbedtls_ssl_config_init(conf); if( (ret = mbedtls_ssl_config_defaults( conf, val_bool(server) ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, 0 )) != 0 ){ mbedtls_ssl_config_free(conf); return ssl_error( ret ); } mbedtls_ssl_conf_rng( conf, mbedtls_ctr_drbg_random, &ctr_drbg ); return alloc_abstract( k_ssl_conf, conf ); } static value conf_close( value config ) { mbedtls_ssl_config *conf; val_check_kind(config,k_ssl_conf); conf = val_conf(config); mbedtls_ssl_config_free(conf); val_kind(config) = NULL; return val_true; } static value conf_set_ca( value config, value cert ) { val_check_kind(config,k_ssl_conf); if( !val_is_null(cert) ) val_check_kind(cert,k_cert); mbedtls_ssl_conf_ca_chain( val_conf(config), val_is_null(cert) ? NULL : val_cert(cert), NULL ); return val_true; } static value conf_set_verify( value config, value b ) { val_check_kind(config, k_ssl_conf); if( !val_is_null(b) ) val_check(b, bool); if( val_is_null(b) ) mbedtls_ssl_conf_authmode(val_conf(config), MBEDTLS_SSL_VERIFY_OPTIONAL); else if( val_bool(b) ) mbedtls_ssl_conf_authmode(val_conf(config), MBEDTLS_SSL_VERIFY_REQUIRED); else mbedtls_ssl_conf_authmode(val_conf(config), MBEDTLS_SSL_VERIFY_NONE); return val_true; } static value conf_set_cert( value config, value cert, value key ) { int r; val_check_kind(config,k_ssl_conf); val_check_kind(cert,k_cert); val_check_kind(key,k_pkey); if( (r = mbedtls_ssl_conf_own_cert(val_conf(config), val_cert(cert), val_pkey(key))) != 0 ) return ssl_error(r); return val_true; } static int sni_callback( void *arg, mbedtls_ssl_context *ctx, const unsigned char *name, size_t len ){ if( name && arg ){ value ret = val_call1((value)arg, alloc_string((const char*)name)) ; if( !val_is_null(ret) ){ // TODO authmode and ca return mbedtls_ssl_set_hs_own_cert( ctx, val_cert(val_field(ret, val_id("cert"))), val_pkey(val_field(ret, val_id("key"))) ); } } return -1; } static value conf_set_servername_callback( value config, value cb ){ val_check_kind(config,k_ssl_conf); val_check_function(cb,1); mbedtls_ssl_conf_sni( val_conf(config), sni_callback, (void *)cb ); return val_true; } static value cert_load_file(value file){ int r; mbedtls_x509_crt *x; value v; val_check(file,string); x = (mbedtls_x509_crt *)alloc(sizeof(mbedtls_x509_crt)); mbedtls_x509_crt_init( x ); if( (r = mbedtls_x509_crt_parse_file(x, val_string(file))) < 0 ){ return ssl_error(r); } v = alloc_abstract(k_cert, x); val_gc(v,free_cert); return v; } static value cert_load_path(value path){ int r; mbedtls_x509_crt *x; value v; val_check(path,string); x = (mbedtls_x509_crt *)alloc(sizeof(mbedtls_x509_crt)); mbedtls_x509_crt_init( x ); if( (r = mbedtls_x509_crt_parse_path(x, val_string(path))) < 0 ){ return ssl_error(r); } v = alloc_abstract(k_cert, x); val_gc(v,free_cert); return v; } static value cert_load_defaults(){ #if defined(NEKO_WINDOWS) value v; HCERTSTORE store; PCCERT_CONTEXT cert; mbedtls_x509_crt *chain = (mbedtls_x509_crt *)alloc(sizeof(mbedtls_x509_crt)); mbedtls_x509_crt_init( chain ); if( store = CertOpenSystemStore(0, (LPCSTR)"Root") ){ cert = NULL; while( cert = CertEnumCertificatesInStore(store, cert) ) mbedtls_x509_crt_parse_der( chain, (unsigned char *)cert->pbCertEncoded, cert->cbCertEncoded ); CertCloseStore(store, 0); } v = alloc_abstract(k_cert, chain); val_gc(v,free_cert); return v; #elif defined(NEKO_MAC) CFMutableDictionaryRef search; CFArrayRef result; SecKeychainRef keychain; SecCertificateRef item; CFDataRef dat; value v; mbedtls_x509_crt *chain = NULL; // Load keychain if( SecKeychainOpen("/System/Library/Keychains/SystemRootCertificates.keychain",&keychain) != errSecSuccess ) return val_null; // Search for certificates search = CFDictionaryCreateMutable( NULL, 0, NULL, NULL ); CFDictionarySetValue( search, kSecClass, kSecClassCertificate ); CFDictionarySetValue( search, kSecMatchLimit, kSecMatchLimitAll ); CFDictionarySetValue( search, kSecReturnRef, kCFBooleanTrue ); CFDictionarySetValue( search, kSecMatchSearchList, CFArrayCreate(NULL, (const void **)&keychain, 1, NULL) ); if( SecItemCopyMatching( search, (CFTypeRef *)&result ) == errSecSuccess ){ CFIndex n = CFArrayGetCount( result ); for( CFIndex i = 0; i < n; i++ ){ item = (SecCertificateRef)CFArrayGetValueAtIndex( result, i ); // Get certificate in DER format dat = SecCertificateCopyData( item ); if( dat ){ if( chain == NULL ){ chain = (mbedtls_x509_crt *)alloc(sizeof(mbedtls_x509_crt)); mbedtls_x509_crt_init( chain ); } mbedtls_x509_crt_parse_der( chain, (unsigned char *)CFDataGetBytePtr(dat), CFDataGetLength(dat) ); CFRelease( dat ); } } } CFRelease(keychain); if( chain != NULL ){ v = alloc_abstract(k_cert, chain); val_gc(v,free_cert); return v; }else{ return val_null; } #else return val_null; #endif } static value asn1_buf_to_string( mbedtls_asn1_buf *dat ){ unsigned int i, c; char *b; value buf = alloc_empty_string( dat->len ); b = val_string(buf); for( i = 0; i < dat->len; i++ ) { c = dat->p[i]; if( c < 32 || c == 127 || ( c > 128 && c < 160 ) ) b[i] = '?'; else b[i] = c; } return buf; } static value cert_get_subject( value cert, value objname ){ mbedtls_x509_crt *crt; mbedtls_x509_name *obj; int r; const char *oname, *rname; val_check_kind(cert,k_cert); val_check(objname, string); crt = val_cert(cert); obj = &crt->subject; if( obj == NULL ) neko_error(); rname = val_string(objname); while( obj != NULL ){ r = mbedtls_oid_get_attr_short_name( &obj->oid, &oname ); if( r == 0 && strcmp( oname, rname ) == 0 ) return asn1_buf_to_string( &obj->val ); obj = obj->next; } return val_null; } static value cert_get_issuer(value cert, value objname){ mbedtls_x509_crt *crt; mbedtls_x509_name *obj; int r; const char *oname, *rname; val_check_kind(cert,k_cert); val_check(objname, string); crt = val_cert(cert); obj = &crt->issuer; if( obj == NULL ) neko_error(); rname = val_string(objname); while( obj != NULL ){ r = mbedtls_oid_get_attr_short_name( &obj->oid, &oname ); if( r == 0 && strcmp( oname, rname ) == 0 ) return asn1_buf_to_string( &obj->val ); obj = obj->next; } return val_null; } static value cert_get_altnames( value cert ){ mbedtls_x509_crt *crt; mbedtls_asn1_sequence *cur; value l = NULL, first = NULL; val_check_kind(cert, k_cert); crt = val_cert(cert); #if MBEDTLS_VERSION_MAJOR >= 3 if( mbedtls_x509_crt_has_ext_type( crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ) ){ #else if( crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ){ #endif cur = &crt->subject_alt_names; while( cur != NULL ){ value l2 = alloc_array(2); val_array_ptr(l2)[0] = asn1_buf_to_string(&cur->buf); val_array_ptr(l2)[1] = val_null; if (first == NULL) first = l2; else val_array_ptr(l)[1] = l2; l = l2; cur = cur->next; } } return (first==NULL)?val_null:first; } static value x509_time_to_array( mbedtls_x509_time *t ){ value v; if( !t ) neko_error(); v = alloc_array(6); val_array_ptr(v)[0] = alloc_int(t->year); val_array_ptr(v)[1] = alloc_int(t->mon); val_array_ptr(v)[2] = alloc_int(t->day); val_array_ptr(v)[3] = alloc_int(t->hour); val_array_ptr(v)[4] = alloc_int(t->min); val_array_ptr(v)[5] = alloc_int(t->sec); return v; } static value cert_get_notbefore(value cert){ mbedtls_x509_crt *crt; val_check_kind(cert, k_cert); crt = val_cert(cert); if( !crt ) neko_error(); return x509_time_to_array( &crt->valid_from ); } static value cert_get_notafter(value cert){ mbedtls_x509_crt *crt; val_check_kind(cert, k_cert); crt = val_cert(cert); if( !crt ) neko_error(); return x509_time_to_array( &crt->valid_to ); } static value cert_get_next( value cert ){ mbedtls_x509_crt *crt; value v; val_check_kind(cert,k_cert); crt = (mbedtls_x509_crt *)val_cert(cert); crt = crt->next; if( crt == NULL ) return val_null; v = alloc_abstract(k_cert, crt); return v; } static value cert_add_pem( value cert, value data ){ mbedtls_x509_crt *crt; int r, len; unsigned char *buf; val_check(data,string); if( !val_is_null(cert) ){ val_check_kind(cert,k_cert); crt = val_cert(cert); if( !crt ) neko_error(); }else{ crt = (mbedtls_x509_crt *)alloc(sizeof(mbedtls_x509_crt)); mbedtls_x509_crt_init( crt ); cert = alloc_abstract(k_cert, crt); val_gc(cert,free_cert); } len = val_strlen(data)+1; buf = (unsigned char *)alloc(len); memcpy(buf, val_string(data), len-1); buf[len-1] = '\0'; if( (r = mbedtls_x509_crt_parse(crt, buf, len)) < 0 ) return ssl_error(r); return cert; } static value cert_add_der( value cert, value data ){ mbedtls_x509_crt *crt; int r; val_check(data,string); if( !val_is_null(cert) ){ val_check_kind(cert,k_cert); crt = val_cert(cert); if( !crt ) neko_error(); }else{ crt = (mbedtls_x509_crt *)alloc(sizeof(mbedtls_x509_crt)); mbedtls_x509_crt_init( crt ); cert = alloc_abstract(k_cert, crt); val_gc(cert,free_cert); } if( (r = mbedtls_x509_crt_parse_der(crt, (const unsigned char*)val_string(data), val_strlen(data))) < 0 ) return ssl_error(r); return cert; } static value key_from_der( value data, value pub ){ mbedtls_pk_context *pk; int r; value v; val_check(data, string); val_check(pub, bool); pk = (mbedtls_pk_context *)alloc(sizeof(mbedtls_pk_context)); mbedtls_pk_init(pk); if( val_bool(pub) ) r = mbedtls_pk_parse_public_key( pk, (const unsigned char*)val_string(data), val_strlen(data) ); else #if MBEDTLS_VERSION_MAJOR >= 3 r = mbedtls_pk_parse_key( pk, (const unsigned char*)val_string(data), val_strlen(data), NULL, 0, mbedtls_ctr_drbg_random, &ctr_drbg ); #else r = mbedtls_pk_parse_key( pk, (const unsigned char*)val_string(data), val_strlen(data), NULL, 0 ); #endif if( r != 0 ){ mbedtls_pk_free(pk); return ssl_error(r); } v = alloc_abstract(k_pkey, pk); val_gc(v,free_pkey); return v; } static value key_from_pem(value data, value pub, value pass){ mbedtls_pk_context *pk; int r, len; value v; unsigned char *buf; val_check(data, string); val_check(pub, bool); if (!val_is_null(pass)) val_check(pass, string); len = val_strlen(data)+1; buf = (unsigned char *)alloc(len); memcpy(buf, val_string(data), len-1); buf[len-1] = '\0'; pk = (mbedtls_pk_context *)alloc(sizeof(mbedtls_pk_context)); mbedtls_pk_init(pk); if( val_bool(pub) ) r = mbedtls_pk_parse_public_key( pk, buf, len ); #if MBEDTLS_VERSION_MAJOR >= 3 else if( val_is_null(pass) ) r = mbedtls_pk_parse_key( pk, buf, len, NULL, 0, mbedtls_ctr_drbg_random, &ctr_drbg ); else r = mbedtls_pk_parse_key( pk, buf, len, (const unsigned char*)val_string(pass), val_strlen(pass), mbedtls_ctr_drbg_random, &ctr_drbg ); #else else if( val_is_null(pass) ) r = mbedtls_pk_parse_key( pk, buf, len, NULL, 0 ); else r = mbedtls_pk_parse_key( pk, buf, len, (const unsigned char*)val_string(pass), val_strlen(pass) ); #endif if( r != 0 ){ mbedtls_pk_free(pk); return ssl_error(r); } v = alloc_abstract(k_pkey,pk); val_gc(v,free_pkey); return v; } static value dgst_make(value data, value alg){ const mbedtls_md_info_t *md; int r = -1; value out; val_check(data, string); val_check(alg, string); md = mbedtls_md_info_from_string(val_string(alg)); if( md == NULL ){ val_throw(alloc_string("Invalid hash algorithm")); return val_null; } out = alloc_empty_string( mbedtls_md_get_size(md) ); if( (r = mbedtls_md( md, (const unsigned char *)val_string(data), val_strlen(data), (unsigned char *)val_string(out) )) != 0 ) return ssl_error(r); return out; } static value dgst_sign(value data, value key, value alg){ const mbedtls_md_info_t *md; int r = -1; size_t olen = 0; value out; unsigned char *buf; unsigned char hash[32]; val_check(data, string); val_check_kind(key, k_pkey); val_check(alg, string); md = mbedtls_md_info_from_string(val_string(alg)); if( md == NULL ){ val_throw(alloc_string("Invalid hash algorithm")); return val_null; } if( (r = mbedtls_md( md, (const unsigned char *)val_string(data), val_strlen(data), hash )) != 0 ) return ssl_error(r); #if MBEDTLS_VERSION_MAJOR >= 3 out = alloc_empty_string(MBEDTLS_PK_SIGNATURE_MAX_SIZE); #else out = alloc_empty_string(MBEDTLS_MPI_MAX_SIZE); #endif buf = (unsigned char *)val_string(out); #if MBEDTLS_VERSION_MAJOR >= 3 if( (r = mbedtls_pk_sign( val_pkey(key), mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), buf, MBEDTLS_PK_SIGNATURE_MAX_SIZE, &olen, mbedtls_ctr_drbg_random, &ctr_drbg )) != 0 ) #else if( (r = mbedtls_pk_sign( val_pkey(key), mbedtls_md_get_type(md), hash, 0, buf, &olen, mbedtls_ctr_drbg_random, &ctr_drbg )) != 0 ) #endif return ssl_error(r); buf[olen] = 0; val_set_size(out, olen); return out; } static value dgst_verify( value data, value sign, value key, value alg ){ const mbedtls_md_info_t *md; int r = -1; unsigned char hash[32]; val_check(data, string); val_check(sign, string); val_check_kind(key, k_pkey); val_check(alg, string); md = mbedtls_md_info_from_string(val_string(alg)); if( md == NULL ){ val_throw(alloc_string("Invalid hash algorithm")); return val_null; } if( (r = mbedtls_md( md, (const unsigned char *)val_string(data), val_strlen(data), hash )) != 0 ) return ssl_error(r); if( (r = mbedtls_pk_verify( val_pkey(key), mbedtls_md_get_type(md), hash, 0, (unsigned char *)val_string(sign), val_strlen(sign) )) != 0 ) return val_false; return val_true; } #if _MSC_VER static void threading_mutex_init_alt( mbedtls_threading_mutex_t *mutex ){ if( mutex == NULL ) return; InitializeCriticalSection( &mutex->cs ); mutex->is_valid = 1; } static void threading_mutex_free_alt( mbedtls_threading_mutex_t *mutex ){ if( mutex == NULL || !mutex->is_valid ) return; DeleteCriticalSection( &mutex->cs ); mutex->is_valid = 0; } static int threading_mutex_lock_alt( mbedtls_threading_mutex_t *mutex ){ if( mutex == NULL || !mutex->is_valid ) return( MBEDTLS_ERR_THREADING_BAD_INPUT_DATA ); EnterCriticalSection( &mutex->cs ); return( 0 ); } static int threading_mutex_unlock_alt( mbedtls_threading_mutex_t *mutex ){ if( mutex == NULL || !mutex->is_valid ) return( MBEDTLS_ERR_THREADING_BAD_INPUT_DATA ); LeaveCriticalSection( &mutex->cs ); return( 0 ); } #endif void ssl_main() { #if _MSC_VER mbedtls_threading_set_alt( threading_mutex_init_alt, threading_mutex_free_alt, threading_mutex_lock_alt, threading_mutex_unlock_alt ); #endif // Init RNG mbedtls_entropy_init( &entropy ); mbedtls_ctr_drbg_init( &ctr_drbg ); mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0 ); #ifdef MBEDTLS_PSA_CRYPTO_C psa_crypto_init(); #endif } DEFINE_PRIM( ssl_new, 1 ); DEFINE_PRIM( ssl_close, 1 ); DEFINE_PRIM( ssl_handshake, 1 ); DEFINE_PRIM( ssl_set_socket, 2 ); DEFINE_PRIM( ssl_set_hostname, 2 ); DEFINE_PRIM( ssl_get_peer_certificate, 1 ); DEFINE_PRIM( ssl_get_verify_result, 1 ); DEFINE_PRIM( ssl_send_char, 2 ); DEFINE_PRIM( ssl_send, 4 ); DEFINE_PRIM( ssl_write, 2 ); DEFINE_PRIM( ssl_recv_char, 1 ); DEFINE_PRIM( ssl_recv, 4 ); DEFINE_PRIM( ssl_read, 1 ); DEFINE_PRIM( conf_new, 1 ); DEFINE_PRIM( conf_close, 1 ); DEFINE_PRIM( conf_set_ca, 2 ); DEFINE_PRIM( conf_set_verify, 2 ); DEFINE_PRIM( conf_set_cert, 3 ); DEFINE_PRIM( conf_set_servername_callback, 2 ); DEFINE_PRIM( cert_load_defaults, 0 ); DEFINE_PRIM( cert_load_file, 1 ); DEFINE_PRIM( cert_load_path, 1 ); DEFINE_PRIM( cert_get_subject, 2 ); DEFINE_PRIM( cert_get_issuer, 2 ); DEFINE_PRIM( cert_get_altnames, 1 ); DEFINE_PRIM( cert_get_notbefore, 1 ); DEFINE_PRIM( cert_get_notafter, 1 ); DEFINE_PRIM( cert_get_next, 1 ); DEFINE_PRIM( cert_add_pem, 2 ); DEFINE_PRIM( cert_add_der, 2 ); DEFINE_PRIM( key_from_pem, 3 ); DEFINE_PRIM( key_from_der, 2 ); DEFINE_PRIM( dgst_make, 2 ); DEFINE_PRIM( dgst_sign, 3 ); DEFINE_PRIM( dgst_verify, 4 ); DEFINE_ENTRY_POINT(ssl_main); neko-2-4-0/libs/ssl/threading_alt.h000066400000000000000000000002111464615675700171760ustar00rootroot00000000000000#define WIN32_LEAN_AND_MEAN #include typedef struct { CRITICAL_SECTION cs; char is_valid; } mbedtls_threading_mutex_t; neko-2-4-0/libs/std/000077500000000000000000000000001464615675700142175ustar00rootroot00000000000000neko-2-4-0/libs/std/CMakeLists.txt000066400000000000000000000010111464615675700167500ustar00rootroot00000000000000 add_library(std.ndll MODULE buffer.c date.c file.c init.c int32.c math.c string.c random.c serialize.c socket.c sys.c xml.c module.c md5.c unicode.c utf8.c memory.c misc.c thread.c process.c elf_update.c ) target_link_libraries(std.ndll sha1 libneko ) if(WIN32) target_link_libraries(std.ndll ws2_32) endif() set_target_properties(std.ndll PROPERTIES PREFIX "" OUTPUT_NAME std SUFFIX .ndll ) install ( TARGETS std.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/std/UCase.hx000066400000000000000000000034471464615675700155700ustar00rootroot00000000000000// haxe program to generate unicase.c from UnicodeData.txt class UCase { static function main(){ var h = sys.io.File.getContent("UnicodeData.txt"); var low = 0, up = 0, max = 0; var a = [], b = []; var bits = 6; for( l in h.split("\n") ) { var l = l.split(";"); var code = Std.parseInt("0x"+l[0]); if( code == 0 ) continue; if( l[13] != "" ) { var k = a[code >> bits]; if( k == null ) a[code>>bits] = k = [for( i in 0...1<> bits]; if( k == null ) b[code>>bits] = k = [for( i in 0...1< max ) max = code; } var sz = a.length; for( i in 0...a.length ) if( a[i] != null ) sz += a[i].length; var bsz = b.length; for( i in 0...b.length ) if( b[i] != null ) bsz += b[i].length; Sys.print("#define UL_BITS "+bits+"\n"); Sys.print("#define UL_SIZE "+(1< /**

Buffer

A buffer can store any value as a string and will only allocate the total needed space when requested. It makes a copy of each value when stored so modifying them after is not a problem.

**/ DEFINE_KIND(k_buffer); /** buffer_new : void -> 'buffer Allocate a new empty buffer **/ static value buffer_new() { buffer b = alloc_buffer(NULL); return alloc_abstract(k_buffer,b); } /** buffer_add : 'buffer -> any -> void Add a value to a buffer **/ static value buffer_add( value b, value v ) { val_check_kind(b,k_buffer); val_buffer( (buffer)val_data(b), v ); return val_true; } /** buffer_add_char : 'buffer -> c:int -> void Add a single char to a buffer. Error if [c] is not in the 0..255 range **/ static value buffer_add_char( value b, value c ) { val_check_kind(b,k_buffer); val_check(c,int); if( val_int(c) < 0 || val_int(c) > 255 ) neko_error(); buffer_append_char( (buffer)val_data(b), (char)(unsigned char)val_int(c) ); return val_true; } /** buffer_add_sub : 'buffer -> s:string -> p:int -> l:int -> void Add [l] characters of the string [s] starting at position [p]. An error occurs if out of string bounds. **/ static value buffer_add_sub( value b, value v, value p, value l ) { val_check_kind(b,k_buffer); val_check(v,string); val_check(p,int); val_check(l,int); if( val_int(p) < 0 || val_int(l) < 0 ) neko_error(); if( val_strlen(v) < val_int(p) || val_strlen(v) < val_int(p) + val_int(l) ) neko_error(); buffer_append_sub( (buffer)val_data(b), val_string(v) + val_int(p) , val_int(l) ); return val_true; } /** buffer_string : 'buffer -> string Build and return the string built with the buffer **/ static value buffer_string( value b ) { val_check_kind(b,k_buffer); return buffer_to_string( (buffer)val_data(b) ); } /** buffer_reset : 'buffer -> void Make the buffer empty **/ static value buffer_reset( value b ) { val_check_kind(b,k_buffer); val_data(b) = alloc_buffer(NULL); return val_true; } /** buffer_get_length : 'buffer -> int Return the number of bytes currently stored into the buffer **/ static value buffer_get_length( value b ) { val_check_kind(b,k_buffer); return alloc_best_int(buffer_length((buffer)val_data(b))); } DEFINE_PRIM(buffer_new,0); DEFINE_PRIM(buffer_add,2); DEFINE_PRIM(buffer_add_char,2); DEFINE_PRIM(buffer_add_sub,4); DEFINE_PRIM(buffer_string,1); DEFINE_PRIM(buffer_reset,1); DEFINE_PRIM(buffer_get_length,1); /* ************************************************************************ */ neko-2-4-0/libs/std/date.c000066400000000000000000000214041464615675700153010ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include /**

Date

Date are using standard C functions in order to manipulate a 32 bit integer. Dates are then represented as the number of seconds elapsed since 1st January 1970.

**/ extern field id_h; extern field id_m; extern field id_s; extern field id_y; extern field id_d; #ifdef NEKO_WINDOWS static struct tm *localtime_r( time_t *t, struct tm *r ) { struct tm *r2 = localtime(t); if( r2 == NULL ) return NULL; *r = *r2; return r; } static struct tm *gmtime_r( time_t *t, struct tm *r ) { struct tm *r2 = gmtime(t); if( r2 == NULL ) return NULL; *r = *r2; return r; } #endif /** date_now : void -> 'int32 Return current date and time **/ static value date_now() { int t = (int)time(NULL); return alloc_int32(t); } /** date_new : string? -> 'int32 Parse a date format. The following formats are accepted :
  • [null] : return current date and time
  • [YYYY-MM-DD HH:MM:SS] : full date and time
  • [YYYY-MM-DD] : date only (time will be set to midnight)
  • [HH:MM:SS] : this represent an elapsed time. It will be corrected with timezone so you can subtract it from a date.
**/ static value date_new( value s ) { int o = 0; if( val_is_null(s) ) o = (int)time(NULL); else { struct tm t; bool recal = true; val_check(s,string); memset(&t,0,sizeof(struct tm)); switch( val_strlen(s) ) { case 19: sscanf(val_string(s),"%4d-%2d-%2d %2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec); t.tm_isdst = -1; break; case 8: sscanf(val_string(s),"%2d:%2d:%2d",&t.tm_hour,&t.tm_min,&t.tm_sec); o = t.tm_sec + t.tm_min * 60 + t.tm_hour * 60 * 60; recal = false; break; case 10: sscanf(val_string(s),"%4d-%2d-%2d",&t.tm_year,&t.tm_mon,&t.tm_mday); t.tm_isdst = -1; break; default: { buffer b = alloc_buffer("Invalid date format : "); val_buffer(b,s); bfailure(b); } } if( recal ) { t.tm_year -= 1900; t.tm_mon--; o = (int)mktime(&t); } } return alloc_int32(o); } #ifdef NEKO_WINDOWS static char VALID_FORMAT_CODES[] = "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%"; #endif /** date_format : #int32 -> fmt:string? -> string Format a date using [strftime]. If [fmt] is [null] then default format is used **/ static value date_format( value o, value fmt ) { char buf[128]; struct tm t; time_t d; val_check(o,any_int); if( val_is_null(fmt) ) fmt = alloc_string("%Y-%m-%d %H:%M:%S"); val_check(fmt,string); d = val_any_int(o); if( localtime_r(&d,&t) == NULL ) neko_error(); #ifdef NEKO_WINDOWS int len = val_strlen(fmt); const char* str = val_string(fmt); int i = 0; while (i < len) { if (str[i] != '%') { i++; continue; } i++; if (str[i] == '#') { i++; } if (str[i] == 'E' || str[i] == 'O') { i++; } bool is_valid = false; const char* format_code = VALID_FORMAT_CODES; while (*format_code) { if (*format_code == str[i]) { is_valid = true; break; } format_code++; } if (!is_valid) { neko_error(); } i++; } #endif if( strftime(buf,127,val_string(fmt),&t) == 0 ) neko_error(); return alloc_string(buf); } /** date_utc_format : #int32 -> fmt:string? -> string Format a date in UTC using [strftime]. If [fmt] is [null] then default format is used **/ static value date_utc_format( value o, value fmt ) { char buf[128]; struct tm t; time_t d; val_check(o,any_int); if( val_is_null(fmt) ) fmt = alloc_string("%Y-%m-%d %H:%M:%S"); val_check(fmt,string); d = val_any_int(o); if( gmtime_r(&d,&t) == NULL ) neko_error(); if( strftime(buf,127,val_string(fmt),&t) == 0 ) neko_error(); return alloc_string(buf); } /** date_set_hour : #int32 -> h:int -> m:int -> s:int -> 'int32 Change the time of a date. Return the modified date **/ static value date_set_hour( value o, value h, value m, value s ) { struct tm t; time_t d; val_check(o,any_int); val_check(h,int); val_check(m,int); val_check(s,int); d = val_any_int(o); if( localtime_r(&d,&t) == NULL ) neko_error(); t.tm_hour = val_int(h); t.tm_min = val_int(m); t.tm_sec = val_int(s); d = mktime(&t); if( d == -1 ) neko_error(); return alloc_int32((int)d); } /** date_set_day : #int32 -> y:int -> m:int -> d:int -> 'int32 Change the day of a date. Return the modified date **/ static value date_set_day( value o, value y, value m, value d ) { struct tm t; time_t date; val_check(o,any_int); val_check(y,int); val_check(m,int); val_check(d,int); date = val_any_int(o); if( localtime_r(&date,&t) == NULL ) neko_error(); t.tm_year = val_int(y) - 1900; t.tm_mon = val_int(m) - 1; t.tm_mday = val_int(d); date = mktime(&t); if( date == -1 ) neko_error(); return alloc_int32((int)date); } /** date_get_day : #int32 -> { y => int, m => int, d => int } Return the year month and day of a date **/ static value date_get_day( value o ) { value r; struct tm t; time_t d; val_check(o,any_int); d = val_any_int(o); if( localtime_r(&d,&t) == NULL ) neko_error(); r = alloc_object(NULL); alloc_field(r,id_y,alloc_int(t.tm_year + 1900)); alloc_field(r,id_m,alloc_int(t.tm_mon + 1)); alloc_field(r,id_d,alloc_int(t.tm_mday)); return r; } /** date_get_utc_day : #int32 -> { y => int, m => int, d => int } Return the year month and day of a date in UTC **/ static value date_get_utc_day( value o ) { value r; struct tm t; time_t d; val_check(o,any_int); d = val_any_int(o); if( gmtime_r(&d,&t) == NULL ) neko_error(); r = alloc_object(NULL); alloc_field(r,id_y,alloc_int(t.tm_year + 1900)); alloc_field(r,id_m,alloc_int(t.tm_mon + 1)); alloc_field(r,id_d,alloc_int(t.tm_mday)); return r; } /** date_get_hour : #int32 -> { h => int, m => int, s => int } Return the hour minutes and seconds of a date **/ static value date_get_hour( value o ) { value r; struct tm t; time_t d; val_check(o,any_int); d = val_any_int(o); if( localtime_r(&d,&t) == NULL ) neko_error(); r = alloc_object(NULL); alloc_field(r,id_h,alloc_int(t.tm_hour)); alloc_field(r,id_m,alloc_int(t.tm_min)); alloc_field(r,id_s,alloc_int(t.tm_sec)); return r; } /** date_get_utc_hour : #int32 -> { h => int, m => int, s => int } Return the hour minutes and seconds of a date in UTC **/ static value date_get_utc_hour( value o ) { value r; struct tm t; time_t d; val_check(o,any_int); d = val_any_int(o); if( gmtime_r(&d,&t) == NULL ) neko_error(); r = alloc_object(NULL); alloc_field(r,id_h,alloc_int(t.tm_hour)); alloc_field(r,id_m,alloc_int(t.tm_min)); alloc_field(r,id_s,alloc_int(t.tm_sec)); return r; } /** date_get_tz : #int32 -> int Return the timezone offset from UTC (in minutes) for the given date **/ static value date_get_tz( value o ) { struct tm local; struct tm gmt; int diff; time_t raw; val_check(o,any_int); raw = val_any_int(o); if( localtime_r(&raw, &local) == NULL || gmtime_r(&raw, &gmt) == NULL ) neko_error(); diff = (local.tm_hour - gmt.tm_hour) * 60 + (local.tm_min - gmt.tm_min); // adjust for different days/years if( gmt.tm_year > local.tm_year || gmt.tm_yday > local.tm_yday ) diff -= 24 * 60; else if( gmt.tm_year < local.tm_year || gmt.tm_yday < local.tm_yday ) diff += 24 * 60; return alloc_int(diff); } DEFINE_PRIM(date_now,0); DEFINE_PRIM(date_new,1); DEFINE_PRIM(date_format,2); DEFINE_PRIM(date_utc_format,2); DEFINE_PRIM(date_set_hour,4); DEFINE_PRIM(date_set_day,4); DEFINE_PRIM(date_get_hour,1); DEFINE_PRIM(date_get_utc_hour,1); DEFINE_PRIM(date_get_day,1); DEFINE_PRIM(date_get_utc_day,1); DEFINE_PRIM(date_get_tz,1); /* ************************************************************************ */ neko-2-4-0/libs/std/elf_update.c000066400000000000000000000054731464615675700165040ustar00rootroot00000000000000/* * Copyright (C)2015-2017 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "neko_elf.h" static value elf_update_section_header_for_bytecode(value _file, value _interp_size, value _bytecode_size) { /* This function is a no-op on non-ELF platforms... */ #ifdef SEPARATE_SECTION_FOR_BYTECODE FILE *exe; int interp_size, bytecode_size; char buf[size_Shdr], *file; int bytecode_sec_idx; val_check(_file,string); val_check(_interp_size,int); val_check(_bytecode_size,int); file = val_string(_file); interp_size = val_int(_interp_size); bytecode_size = val_int(_bytecode_size); if ( interp_size%4 != 0 ) { return val_false; } /* Open the file to update the elf nekobytecode section header... */ exe = fopen(file,"r+b"); if( exe == NULL ) return val_false; /* First read the elf header... */ if ( val_true != elf_read_header(exe) ) goto failed; /* Find the right section header... */ bytecode_sec_idx = elf_find_bytecode_section(exe); if ( -1 == bytecode_sec_idx ) goto failed; /* Now that we have the right section header, update it... */ if ( val_true != elf_read_section(exe,bytecode_sec_idx,buf) ) goto failed; elf_set_Shdr(buf,sh_type,SHT_PROGBITS); elf_set_Shdr(buf,sh_flags,elf_get_Shdr(buf,sh_flags) & (SHF_MASKOS|SHF_MASKPROC)); elf_set_Shdr(buf,sh_addr,0); elf_set_Shdr(buf,sh_offset,interp_size); elf_set_Shdr(buf,sh_size,bytecode_size); elf_set_Shdr(buf,sh_addralign,1); elf_set_Shdr(buf,sh_entsize,0); /* ...and write it back... */ if ( val_true != elf_write_section(exe,bytecode_sec_idx,buf) ) goto failed; elf_free_section_string_table(); fclose(exe); return val_true; failed: elf_free_section_string_table(); fclose(exe); return val_false; #else return val_true; #endif } DEFINE_PRIM(elf_update_section_header_for_bytecode,3); neko-2-4-0/libs/std/file.c000066400000000000000000000171711464615675700153110ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #ifdef NEKO_WINDOWS # include #endif /**

File

The file api can be used for different kind of file I/O.

**/ typedef struct { value name; FILE *io; } fio; #define val_file(o) ((fio*)val_data(o)) DEFINE_KIND(k_file); static void file_error( const char *msg, fio *f ) { value a = alloc_array(2); val_array_ptr(a)[0] = alloc_string(msg); val_array_ptr(a)[1] = alloc_string(val_string(f->name)); val_throw(a); } /** file_open : f:string -> r:string -> 'file Call the C function [fopen] with the file path and access rights. Return the opened file or throw an exception if the file couldn't be open. **/ static value file_open( value name, value r ) { fio *f; val_check(name,string); val_check(r,string); f = (fio*)alloc(sizeof(fio)); f->name = alloc_string(val_string(name)); f->io = fopen(val_string(name),val_string(r)); if( f->io == NULL ) file_error("file_open",f); return alloc_abstract(k_file,f); } /** file_close : 'file -> void Close an file. Any other operations on this file will fail **/ static value file_close( value o ) { fio *f; val_check_kind(o,k_file); f = val_file(o); fclose(f->io); val_kind(o) = NULL; return val_null; } /** file_name : 'file -> string Return the name of the file which was opened **/ static value file_name( value o ) { val_check_kind(o,k_file); return alloc_string(val_string(val_file(o)->name)); } /** file_write : 'file -> s:string -> p:int -> l:int -> int Write up to [l] chars of string [s] starting at position [p]. Returns the number of chars written which is >= 0. **/ static value file_write( value o, value s, value pp, value n ) { int p, len; fio *f; val_check_kind(o,k_file); val_check(s,string); val_check(pp,int); val_check(n,int); f = val_file(o); p = val_int(pp); len = val_int(n); if( p < 0 || len < 0 || p > val_strlen(s) || p + len > val_strlen(s) ) neko_error(); while( len > 0 ) { int d; POSIX_LABEL(file_write_again); d = (int)fwrite(val_string(s)+p,1,len,f->io); if( d <= 0 ) { HANDLE_FINTR(f->io,file_write_again); file_error("file_write",f); } p += d; len -= d; } return n; } /** file_read : 'file -> s:string -> p:int -> l:int -> int Read up to [l] chars into the string [s] starting at position [p]. Returns the number of chars readed which is > 0 (or 0 if l == 0). **/ static value file_read( value o, value s, value pp, value n ) { fio *f; int p; int len; val_check_kind(o,k_file); val_check(s,string); val_check(pp,int); val_check(n,int); f = val_file(o); p = val_int(pp); len = val_int(n); if( p < 0 || len < 0 || p > val_strlen(s) || p + len > val_strlen(s) ) neko_error(); while( len > 0 ) { int d; POSIX_LABEL(file_read_again); d = (int)fread((char*)val_string(s)+p,1,len,f->io); if( d <= 0 ) { int size = val_int(n) - len; HANDLE_FINTR(f->io,file_read_again); if( size == 0 ) file_error("file_read",f); return alloc_int(size); } p += d; len -= d; } return n; } /** file_write_char : 'file -> c:int -> void Write the char [c]. Error if [c] outside of the range 0..255 **/ static value file_write_char( value o, value c ) { unsigned char cc; fio *f; val_check(c,int); val_check_kind(o,k_file); if( val_int(c) < 0 || val_int(c) > 255 ) neko_error(); cc = (char)val_int(c); f = val_file(o); POSIX_LABEL(write_char_again); if( fwrite(&cc,1,1,f->io) != 1 ) { HANDLE_FINTR(f->io,write_char_again); file_error("file_write_char",f); } return val_null; } /** file_read_char : 'file -> int Read a char from the file. Exception on error **/ static value file_read_char( value o ) { unsigned char cc; fio *f; val_check_kind(o,k_file); f = val_file(o); POSIX_LABEL(read_char_again); if( fread(&cc,1,1,f->io) != 1 ) { HANDLE_FINTR(f->io,read_char_again); file_error("file_read_char",f); } return alloc_int(cc); } /** file_seek : 'file -> pos:int -> mode:int -> void Use [fseek] to move the file pointer. **/ static value file_seek( value o, value pos, value kind ) { fio *f; val_check_kind(o,k_file); val_check(pos,int); val_check(kind,int); f = val_file(o); if( fseek(f->io,val_int(pos),val_int(kind)) != 0 ) file_error("file_seek",f); return val_null; } /** file_tell : 'file -> int Return the current position in the file **/ static value file_tell( value o ) { int p; fio *f; val_check_kind(o,k_file); f = val_file(o); p = ftell(f->io); if( p == -1 ) file_error("file_tell",f); return alloc_int(p); } /** file_eof : 'file -> bool Tell if we have reached the end of the file **/ static value file_eof( value o ) { val_check_kind(o,k_file); return alloc_bool( feof(val_file(o)->io) ); } /** file_flush : 'file -> void Flush the file buffer **/ static value file_flush( value o ) { fio *f; val_check_kind(o,k_file); f = val_file(o); if( fflush( f->io ) != 0 ) file_error("file_flush",f); return val_true; } /** file_contents : f:string -> string Read the content of the file [f] and return it. **/ static value file_contents( value name ) { value s; fio f; int len; int p; val_check(name,string); f.name = name; f.io = fopen(val_string(name),"rb"); if( f.io == NULL ) file_error("file_contents",&f); fseek(f.io,0,SEEK_END); len = ftell(f.io); fseek(f.io,0,SEEK_SET); s = alloc_empty_string(len); p = 0; while( len > 0 ) { int d; POSIX_LABEL(file_contents); d = (int)fread((char*)val_string(s)+p,1,len,f.io); if( d <= 0 ) { HANDLE_FINTR(f.io,file_contents); fclose(f.io); file_error("file_contents",&f); } p += d; len -= d; } fclose(f.io); return s; } #define MAKE_STDIO(k) \ static value file_##k() { \ fio *f; \ f = (fio*)alloc(sizeof(fio)); \ f->name = alloc_string(#k); \ f->io = k; \ return alloc_abstract(k_file,f); \ } \ DEFINE_PRIM(file_##k,0); /** file_stdin : void -> 'file The standard input **/ MAKE_STDIO(stdin); /** file_stdout : void -> 'file The standard output **/ MAKE_STDIO(stdout); /** file_stderr : void -> 'file The standard error output **/ MAKE_STDIO(stderr); DEFINE_PRIM(file_open,2); DEFINE_PRIM(file_close,1); DEFINE_PRIM(file_name,1); DEFINE_PRIM(file_write,4); DEFINE_PRIM(file_read,4); DEFINE_PRIM(file_write_char,2); DEFINE_PRIM(file_read_char,1); DEFINE_PRIM(file_seek,3); DEFINE_PRIM(file_tell,1); DEFINE_PRIM(file_eof,1); DEFINE_PRIM(file_flush,1); DEFINE_PRIM(file_contents,1); /* ************************************************************************ */ neko-2-4-0/libs/std/init.c000066400000000000000000000042561464615675700153350ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include field id_h; field id_m; field id_s; field id_y; field id_d; field id_module; field id_loadmodule; field id_loadprim; field id_done; field id_comment; field id_xml; field id_pcdata; field id_cdata; field id_doctype; field id_serialize; field id_unserialize; DEFINE_ENTRY_POINT(std_main); extern vkind k_file; extern vkind k_socket; extern vkind k_buffer; extern vkind k_thread; void std_main() { id_h = val_id("h"); id_m = val_id("m"); id_s = val_id("s"); id_y = val_id("y"); id_d = val_id("d"); id_loadmodule = val_id("loadmodule"); id_loadprim = val_id("loadprim"); id_module = val_id("__module"); id_done = val_id("done"); id_comment = val_id("comment"); id_xml = val_id("xml"); id_pcdata = val_id("pcdata"); id_cdata = val_id("cdata"); id_doctype = val_id("doctype"); id_serialize = val_id("__serialize"); id_unserialize = val_id("__unserialize"); kind_share(&k_file,"file"); kind_share(&k_socket,"socket"); kind_share(&k_buffer,"buffer"); kind_share(&k_thread,"thread"); } /* ************************************************************************ */ neko-2-4-0/libs/std/int32.c000066400000000000000000000122671464615675700153320ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include /**

Int32

Int32 api is deprecated as of Neko 2.0, which have native support for Int32.

**/ /** int32_new : (#int32 | float) -> 'int32 Allocate an int32 from any number **/ static value int32_new( value v ) { val_check(v,number); return alloc_int32((int)val_number(v)); } /** int32_to_int : #int32 -> int Return the int value if it can be represented using 31 bits. Error either **/ static value int32_to_int( value v ) { int i; val_check(v,any_int); i = val_any_int(v); if( need_32_bits(i) ) neko_error(); return alloc_int(i); } /** int32_to_float : #int32 -> float Return the float value of the integer. **/ static value int32_to_float( value v ) { val_check(v,any_int); return alloc_float(val_any_int(v)); } /** int32_compare : #int32 -> #int32 -> int Compare two integers **/ static value int32_compare( value v1, value v2 ) { int i1, i2; val_check(v1,any_int); val_check(v2,any_int); i1 = val_any_int(v1); i2 = val_any_int(v2); if( i1 == i2 ) return alloc_int(0); else if( i1 > i2 ) return alloc_int(1); else return alloc_int(-1); } #define INT32_OP(op_name,op) \ static value int32_##op_name( value v1, value v2 ) { \ int r; \ val_check(v1,any_int); \ val_check(v2,any_int); \ r = val_any_int(v1) op val_any_int(v2); \ return alloc_best_int(r); \ } \ DEFINE_PRIM(int32_##op_name,2) #define INT32_UNOP(op_name,op) \ static value int32_##op_name( value v ) { \ int r; \ val_check(v,any_int); \ r = op val_any_int(v); \ return alloc_best_int(r); \ } \ DEFINE_PRIM(int32_##op_name,1) #define INT32_OP_ZERO(op_name,op) \ static value int32_##op_name( value v1, value v2 ) { \ int d; \ int r; \ val_check(v1,any_int); \ val_check(v2,any_int); \ d = val_any_int(v2); \ if( d == 0 ) \ neko_error(); \ r = val_any_int(v1) op d; \ return alloc_best_int(r); \ } \ DEFINE_PRIM(int32_##op_name,2) /** int32_ushr : #int32 -> #int32 -> #int32 Perform unsigned right bits-shifting **/ static value int32_ushr( value v1, value v2 ) { int r; val_check(v1,any_int); val_check(v2,any_int); r = ((unsigned int)val_any_int(v1)) >> val_any_int(v2); return alloc_best_int(r); } /** int32_add : #int32 -> #int32 -> #int32 Add two integers **/ INT32_OP(add,+); /** int32_sub : #int32 -> #int32 -> #int32 Subtract two integers **/ INT32_OP(sub,-); /** int32_mul : #int32 -> #int32 -> #int32 Multiply two integers **/ INT32_OP(mul,*); /** int32_div : #int32 -> #int32 -> #int32 Divide two integers. Error on division by 0 **/ INT32_OP_ZERO(div,/); /** int32_shl : #int32 -> #int32 -> #int32 Perform left bit-shifting **/ INT32_OP(shl,<<); /** int32_shr : #int32 -> #int32 -> #int32 Perform right bit-shifting **/ INT32_OP(shr,>>); /** int32_mod : #int32 -> #int32 -> #int32 Return the modulo of one integer by the other. Error on modulo by 0 **/ INT32_OP_ZERO(mod,%); /** int32_neg : #int32 -> #int32 Return the negative value of an integer **/ INT32_UNOP(neg,-); /** int32_complement : #int32 -> #int32 Return the one-complement bitwised integer **/ INT32_UNOP(complement,~); /** int32_or : #int32 -> #int32 -> #int32 Return the bitwise or of two integers **/ INT32_OP(or,|); /** int32_and : #int32 -> #int32 -> #int32 Return the bitwise and of two integers **/ INT32_OP(and,&); /** int32_xor : #int32 -> #int32 -> #int32 Return the bitwise xor of two integers **/ INT32_OP(xor,^); /** int32_address : any -> #int32 Return the address of the value. The address should not be considered constant. It is not unique either unless you are sure you are running on a 32-bit platform. **/ static value int32_address( value v ) { return alloc_best_int((int)(int_val)v); } DEFINE_PRIM(int32_new,1); DEFINE_PRIM(int32_to_int,1); DEFINE_PRIM(int32_to_float,1); DEFINE_PRIM(int32_compare,2); DEFINE_PRIM(int32_ushr,2); DEFINE_PRIM(int32_address,1); /* ************************************************************************ */ neko-2-4-0/libs/std/math.c000066400000000000000000000131341464615675700153160ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include /**

Math

Mathematical functions

**/ #define MATH_PRIM(f) \ value math_##f( value n ) { \ val_check(n,number); \ return alloc_float( f( val_number(n) ) ); \ } \ DEFINE_PRIM(math_##f,1) /** math_atan2 : number -> number -> float Return atan2 calculus **/ static value math_atan2( value a, value b ) { val_check(a,number); val_check(b,number); return alloc_float( atan2(val_number(a),val_number(b)) ); } /** math_pow : number -> number -> float Return power calculus **/ static value math_pow( value a, value b ) { tfloat r; val_check(a,number); val_check(b,number); r = (tfloat)pow(val_number(a),val_number(b)); if( (int)r == r && fabs(r) < (1 << 31) ) return alloc_best_int((int)r); return alloc_float(r); } /** math_abs : number -> number Return absolute value of a number **/ static value math_abs( value n ) { switch( val_type(n) ) { case VAL_INT: return alloc_int( abs(val_int(n)) ); case VAL_INT32: return alloc_int32( abs(val_int32(n)) ); case VAL_FLOAT: return alloc_float( fabs(val_float(n)) ); default: neko_error(); } } /** math_ceil : number -> int Return rounded-up integer **/ static value math_ceil( value n ) { switch( val_type(n) ) { case VAL_INT: case VAL_INT32: return n; case VAL_FLOAT: return alloc_best_int( (int)ceil(val_float(n)) ); default: neko_error(); } } /** math_floor : number -> int Return rounded-down integer **/ static value math_floor( value n ) { switch( val_type(n) ) { case VAL_INT: case VAL_INT32: return n; case VAL_FLOAT: return alloc_best_int( (int)floor(val_float(n)) ); default: neko_error(); } } /** math_round : number -> int Return nearest integer **/ static value math_round( value n ) { switch( val_type(n) ) { case VAL_INT: case VAL_INT32: return n; case VAL_FLOAT: return alloc_best_int( (int)floor(val_float(n) + 0.5) ); default: neko_error(); } } /** math_fceil : number -> number Return rounded-up float without integer overflow **/ static value math_fceil( value n ) { switch( val_type(n) ) { case VAL_INT: case VAL_INT32: return n; case VAL_FLOAT: return alloc_float( ceil(val_float(n)) ); default: neko_error(); } } /** math_ffloor : number -> number Return rounded-down float without integer overflow **/ static value math_ffloor( value n ) { switch( val_type(n) ) { case VAL_INT: case VAL_INT32: return n; case VAL_FLOAT: return alloc_float( floor(val_float(n)) ); default: neko_error(); } } /** math_fround : number -> number Return rounded float without integer overflow **/ static value math_fround( value n ) { switch( val_type(n) ) { case VAL_INT: case VAL_INT32: return n; case VAL_FLOAT: return alloc_float( floor(val_float(n) + 0.5) ); default: neko_error(); } } /** math_int : number -> int Return integer rounded down towards 0 **/ static value math_int( value n ) { switch( val_type(n) ) { case VAL_INT: case VAL_INT32: return n; case VAL_FLOAT: { tfloat v = val_float(n); return alloc_best_int( (int)((v < 0) ? ceil(v) : floor(v)) ); } default: neko_error(); } } #define PI 3.1415926535897932384626433832795 /** math_pi : void -> float Return the value of PI **/ static value math_pi() { return alloc_float(PI); } /** math_sqrt : number -> float Return the square-root **/ MATH_PRIM(sqrt); /** math_atan : number -> float Return the arc-tangent **/ MATH_PRIM(atan); /** math_cos : number -> float Return the cosinus **/ MATH_PRIM(cos); /** math_sin : number -> float Return the sinus **/ MATH_PRIM(sin); /** math_tan : number -> float Return the tangent **/ MATH_PRIM(tan); /** math_log : number -> float Return the logarithm **/ MATH_PRIM(log); /** math_exp : number -> float Return the exponant **/ MATH_PRIM(exp); /** math_acos : number -> float Return the arc-cosinus **/ MATH_PRIM(acos); /** math_asin : number -> float Return the arc-sinus **/ MATH_PRIM(asin); DEFINE_PRIM(math_pi,0); DEFINE_PRIM(math_atan2,2); DEFINE_PRIM(math_pow,2); DEFINE_PRIM(math_abs,1); DEFINE_PRIM(math_ceil,1); DEFINE_PRIM(math_floor,1); DEFINE_PRIM(math_round,1); DEFINE_PRIM(math_fceil,1); DEFINE_PRIM(math_ffloor,1); DEFINE_PRIM(math_fround,1); DEFINE_PRIM(math_int,1); /* ************************************************************************ */ neko-2-4-0/libs/std/md5.c000066400000000000000000000252521464615675700150560ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include "sha1.h" /**

MD5

MD5 digest functions

**/ #ifndef uint8 #define uint8 unsigned char #endif #ifndef uint32 #define uint32 unsigned long int #endif typedef struct { uint32 total[2]; uint32 state[4]; uint8 buffer[64]; } md5_context; #define GET_UINT32(n,b,i) \ { \ (n) = ( (uint32) (b)[(i) ] ) \ | ( (uint32) (b)[(i) + 1] << 8 ) \ | ( (uint32) (b)[(i) + 2] << 16 ) \ | ( (uint32) (b)[(i) + 3] << 24 ); \ } #define PUT_UINT32(n,b,i) \ { \ (b)[(i) ] = (uint8) ( (n) ); \ (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \ (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \ (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \ } static void md5_starts( md5_context *ctx ) { ctx->total[0] = 0; ctx->total[1] = 0; ctx->state[0] = 0x67452301; ctx->state[1] = 0xEFCDAB89; ctx->state[2] = 0x98BADCFE; ctx->state[3] = 0x10325476; } static void md5_process( md5_context *ctx, uint8 data[64] ) { uint32 X[16], A, B, C, D; GET_UINT32( X[0], data, 0 ); GET_UINT32( X[1], data, 4 ); GET_UINT32( X[2], data, 8 ); GET_UINT32( X[3], data, 12 ); GET_UINT32( X[4], data, 16 ); GET_UINT32( X[5], data, 20 ); GET_UINT32( X[6], data, 24 ); GET_UINT32( X[7], data, 28 ); GET_UINT32( X[8], data, 32 ); GET_UINT32( X[9], data, 36 ); GET_UINT32( X[10], data, 40 ); GET_UINT32( X[11], data, 44 ); GET_UINT32( X[12], data, 48 ); GET_UINT32( X[13], data, 52 ); GET_UINT32( X[14], data, 56 ); GET_UINT32( X[15], data, 60 ); #define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) #define P(a,b,c,d,k,s,t) \ { \ a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ } A = ctx->state[0]; B = ctx->state[1]; C = ctx->state[2]; D = ctx->state[3]; #define F(x,y,z) (z ^ (x & (y ^ z))) P( A, B, C, D, 0, 7, 0xD76AA478 ); P( D, A, B, C, 1, 12, 0xE8C7B756 ); P( C, D, A, B, 2, 17, 0x242070DB ); P( B, C, D, A, 3, 22, 0xC1BDCEEE ); P( A, B, C, D, 4, 7, 0xF57C0FAF ); P( D, A, B, C, 5, 12, 0x4787C62A ); P( C, D, A, B, 6, 17, 0xA8304613 ); P( B, C, D, A, 7, 22, 0xFD469501 ); P( A, B, C, D, 8, 7, 0x698098D8 ); P( D, A, B, C, 9, 12, 0x8B44F7AF ); P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); P( B, C, D, A, 11, 22, 0x895CD7BE ); P( A, B, C, D, 12, 7, 0x6B901122 ); P( D, A, B, C, 13, 12, 0xFD987193 ); P( C, D, A, B, 14, 17, 0xA679438E ); P( B, C, D, A, 15, 22, 0x49B40821 ); #undef F #define F(x,y,z) (y ^ (z & (x ^ y))) P( A, B, C, D, 1, 5, 0xF61E2562 ); P( D, A, B, C, 6, 9, 0xC040B340 ); P( C, D, A, B, 11, 14, 0x265E5A51 ); P( B, C, D, A, 0, 20, 0xE9B6C7AA ); P( A, B, C, D, 5, 5, 0xD62F105D ); P( D, A, B, C, 10, 9, 0x02441453 ); P( C, D, A, B, 15, 14, 0xD8A1E681 ); P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); P( A, B, C, D, 9, 5, 0x21E1CDE6 ); P( D, A, B, C, 14, 9, 0xC33707D6 ); P( C, D, A, B, 3, 14, 0xF4D50D87 ); P( B, C, D, A, 8, 20, 0x455A14ED ); P( A, B, C, D, 13, 5, 0xA9E3E905 ); P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); P( C, D, A, B, 7, 14, 0x676F02D9 ); P( B, C, D, A, 12, 20, 0x8D2A4C8A ); #undef F #define F(x,y,z) (x ^ y ^ z) P( A, B, C, D, 5, 4, 0xFFFA3942 ); P( D, A, B, C, 8, 11, 0x8771F681 ); P( C, D, A, B, 11, 16, 0x6D9D6122 ); P( B, C, D, A, 14, 23, 0xFDE5380C ); P( A, B, C, D, 1, 4, 0xA4BEEA44 ); P( D, A, B, C, 4, 11, 0x4BDECFA9 ); P( C, D, A, B, 7, 16, 0xF6BB4B60 ); P( B, C, D, A, 10, 23, 0xBEBFBC70 ); P( A, B, C, D, 13, 4, 0x289B7EC6 ); P( D, A, B, C, 0, 11, 0xEAA127FA ); P( C, D, A, B, 3, 16, 0xD4EF3085 ); P( B, C, D, A, 6, 23, 0x04881D05 ); P( A, B, C, D, 9, 4, 0xD9D4D039 ); P( D, A, B, C, 12, 11, 0xE6DB99E5 ); P( C, D, A, B, 15, 16, 0x1FA27CF8 ); P( B, C, D, A, 2, 23, 0xC4AC5665 ); #undef F #define F(x,y,z) (y ^ (x | ~z)) P( A, B, C, D, 0, 6, 0xF4292244 ); P( D, A, B, C, 7, 10, 0x432AFF97 ); P( C, D, A, B, 14, 15, 0xAB9423A7 ); P( B, C, D, A, 5, 21, 0xFC93A039 ); P( A, B, C, D, 12, 6, 0x655B59C3 ); P( D, A, B, C, 3, 10, 0x8F0CCC92 ); P( C, D, A, B, 10, 15, 0xFFEFF47D ); P( B, C, D, A, 1, 21, 0x85845DD1 ); P( A, B, C, D, 8, 6, 0x6FA87E4F ); P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); P( C, D, A, B, 6, 15, 0xA3014314 ); P( B, C, D, A, 13, 21, 0x4E0811A1 ); P( A, B, C, D, 4, 6, 0xF7537E82 ); P( D, A, B, C, 11, 10, 0xBD3AF235 ); P( C, D, A, B, 2, 15, 0x2AD7D2BB ); P( B, C, D, A, 9, 21, 0xEB86D391 ); #undef F ctx->state[0] += A; ctx->state[1] += B; ctx->state[2] += C; ctx->state[3] += D; } static void md5_update( md5_context *ctx, uint8 *input, uint32 length ) { uint32 left, fill; if( !length ) return; left = ctx->total[0] & 0x3F; fill = 64 - left; ctx->total[0] += length; ctx->total[0] &= 0xFFFFFFFF; if( ctx->total[0] < length ) ctx->total[1]++; if( left && length >= fill ) { memcpy( (void *) (ctx->buffer + left), (void *) input, fill ); md5_process( ctx, ctx->buffer ); length -= fill; input += fill; left = 0; } while( length >= 64 ) { md5_process( ctx, input ); length -= 64; input += 64; } if( length ) { memcpy( (void *) (ctx->buffer + left), (void *) input, length ); } } static uint8 md5_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static void md5_finish( md5_context *ctx, uint8 digest[16] ) { uint32 last, padn; uint32 high, low; uint8 msglen[8]; high = ( ctx->total[0] >> 29 ) | ( ctx->total[1] << 3 ); low = ( ctx->total[0] << 3 ); PUT_UINT32( low, msglen, 0 ); PUT_UINT32( high, msglen, 4 ); last = ctx->total[0] & 0x3F; padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); md5_update( ctx, md5_padding, padn ); md5_update( ctx, msglen, 8 ); PUT_UINT32( ctx->state[0], digest, 0 ); PUT_UINT32( ctx->state[1], digest, 4 ); PUT_UINT32( ctx->state[2], digest, 8 ); PUT_UINT32( ctx->state[3], digest, 12 ); } static void md5_uint( md5_context *m, uint32 i ) { md5_update(m,(unsigned char*)&i,4); } typedef struct stack { uint32 pos; value v; md5_context *m; struct stack *next; } stack; // build some integers that are not neko ones (last bit at 0) // this reduce the possible collisions. // bit 0 = 0 // bit 1-2 = // 00 : const (null, true, false, abstract) // 01 : ref // 10 : fun // 11 : array // // md5-similar objects can still be forged, in particular // due to the fact that no special flag is added to the string // digest in order to keep compatibility with standard md5 // implementation. // // For example md5(null) == md5("\000\000\000\000") static void make_md5_fields( value v, field f, void * ); static void make_md5_rec( md5_context *m, value v, stack *cur ) { switch( val_type(v) ) { case VAL_NULL: md5_uint(m,0); break; case VAL_INT: md5_uint(m,(uint32)(int_val)v); break; case VAL_INT32: md5_uint(m,(uint32)val_int32(v)); break; case VAL_BOOL: md5_uint(m,val_bool(v)?8:16); break; case VAL_FLOAT: { tfloat f = val_float(v); md5_update(m,(unsigned char *)&f,sizeof(tfloat)); } break; case VAL_STRING: md5_update(m,(uint8*)val_string(v),(uint32)val_strlen(v)); break; case VAL_OBJECT: case VAL_ARRAY: { stack loc; stack *s = cur; while( s != NULL ) { if( s->v == v ) { md5_uint(m,(s->pos << 3) | 2); return; } s = s->next; } loc.v = v; loc.pos = cur?(cur->pos+1):0; loc.next = cur; loc.m = m; if( val_is_object(v) ) { val_iter_fields(v,make_md5_fields,&loc); v = (value)((vobject*)v)->proto; if( v != NULL ) make_md5_rec(m,v,&loc); } else { int len = val_array_size(v); md5_uint(m,(len << 3) | 6); while( len-- > 0 ) make_md5_rec(m,val_array_ptr(v)[len],&loc); } } break; case VAL_FUNCTION: md5_uint(m,(val_fun_nargs(v) << 3) | 4); break; case VAL_ABSTRACT: md5_uint(m,24); break; } } static void make_md5_fields( value v, field f, void *c ) { stack *s = (stack*)c; md5_uint(s->m,f); make_md5_rec(s->m,v,s); } /** make_md5 : any -> string Build a MD5 digest (16 bytes binary string) from any value. **/ static value make_md5( value v ) { value out = alloc_empty_string(16); md5_context m; md5_starts(&m); make_md5_rec(&m,v,NULL); md5_finish(&m,(uint8 *)val_string(out)); return out; } /** make_sha1 : string -> pos:int -> len:int -> string Build a SHA1 digest for the given substring **/ static value make_sha1( value s, value p, value l ) { SHA1_CTX ctx; SHA1_DIGEST result; int pp , ll; val_check(s,string); val_check(p,int); val_check(l,int); pp = val_int(p); ll = val_int(l); if( pp < 0 || ll < 0 || pp + ll < 0 || pp + ll > val_strlen(s) ) neko_error(); sha1_init(&ctx); sha1_update(&ctx,(unsigned char*)val_string(s)+pp,ll); sha1_final(&ctx,result); return copy_string( (char*)result, sizeof(SHA1_DIGEST) ); } DEFINE_PRIM(make_md5,1); DEFINE_PRIM(make_sha1,3); /* ************************************************************************ */ neko-2-4-0/libs/std/memory.c000066400000000000000000000124401464615675700156740ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include /**

Memory

An API for memory manipulation and statistics.

**/ typedef struct _vtree { int_val v; struct _vtree *left; struct _vtree *right; } vtree; typedef struct { vtree **t; int s; } vparams; static int mem_cache( void *v, vtree **t ) { vtree *p = *t; vtree *prev = NULL; while( p != NULL ) { if( p->v == (int_val)v ) return 1; prev = p; if( p->v > (int_val)v ) p = p->left; else p = p->right; } p = (vtree*)alloc(sizeof(vtree)); p->v = (int_val)v; p->left = NULL; p->right = NULL; if( prev == NULL ) *t = p; else { if( prev->v > p->v ) prev->left = p; else prev->right = p; } return 0; } static void mem_obj_field( value v, field f, void *_p ); static int mem_module( neko_module *m, vtree **l ); static int mem_size_rec( value v, vtree **l ) { switch( val_type(v) ) { case VAL_INT: case VAL_BOOL: case VAL_NULL: return 0; case VAL_FLOAT: if( mem_cache(v,l) ) return 0; return sizeof(vfloat); case VAL_INT32: if( mem_cache(v,l) ) return 0; return sizeof(vint32); case VAL_STRING: if( mem_cache(v,l) ) return 0; return sizeof(value) + val_strlen(v); case VAL_OBJECT: if( mem_cache(v,l) ) return 0; { vparams p; p.t = l; p.s = sizeof(vobject); val_iter_fields(v,mem_obj_field,&p); if( ((vobject*)v)->proto != NULL ) p.s += mem_size_rec((value)((vobject*)v)->proto,l); return p.s; } case VAL_ARRAY: if( mem_cache(v,l) ) return 0; { int t = sizeof(value); int size = val_array_size(v); int i; t += size * sizeof(value); for(i=0;ienv,l); if( val_tag(v) == VAL_PRIMITIVE ) t += mem_size_rec((value)((vfunction*)v)->module,l); else t += mem_module(((vfunction*)v)->module,l); return t; } case VAL_ABSTRACT: { int t; if( mem_cache(v,l) ) return 0; t = sizeof(vabstract); if( val_kind(v) == neko_kind_module ) t += mem_module((neko_module*)val_data(v),l); else if( val_kind(v) == k_hash ) { vhash *h = (vhash*)val_data(v); int i; t += sizeof(vhash); t += sizeof(hcell*) * h->ncells; for(i=0;incells;i++) { hcell *c = h->cells[i]; while( c != NULL ) { t += sizeof(hcell); t += mem_size_rec(c->key,l); t += mem_size_rec(c->val,l); c = c->next; } } } return t; } default: val_throw(alloc_string("mem_size : Unexpected value")); break; } return 0; } static void mem_obj_field( value v, field f, void *_p ) { vparams *p = (vparams*)_p; p->s += sizeof(objcell); p->s += mem_size_rec(v,p->t); } static int mem_module( neko_module *m, vtree **l ) { int t = 0; unsigned int i; if( mem_cache(m,l) ) return 0; t += sizeof(neko_module); t += m->codesize * sizeof(int_val); t += m->nglobals * sizeof(int_val); for(i=0;inglobals;i++) t += mem_size_rec(m->globals[i],l); t += m->nfields * sizeof(value*); for(i=0;infields;i++) t += mem_size_rec(m->fields[i],l); t += mem_size_rec(m->loader,l); t += mem_size_rec(m->exports,l); t += mem_size_rec(m->dbgtbl,l); if( m->dbgidxs ) t += sizeof(neko_debug) * (m->codesize >> 5); t += mem_size_rec(m->name,l); return t; } /** mem_size : any -> int Calculate the quite precise amount of VM memory reachable from this value **/ static value mem_size( value v ) { vtree *t = NULL; return alloc_int(mem_size_rec(v,&t)); } /** mem_local_size : any -> any array -> int Calculate the quite precise amount of VM memory reachable from this value, without scanning the values contained in the array. **/ static value mem_local_size( value v, value a ) { vtree *t = NULL; int i; val_check(a,array); for(i=0;i /**

Misc

Misc. functions for different usages.

**/ /** float_bytes : number -> bigendian:bool -> string Returns the 4 bytes representation of the number as an IEEE 32-bit float **/ static value float_bytes( value n, value be ) { float f; val_check(n,number); val_check(be,bool); f = (float)val_number(n); if( neko_is_big_endian() != val_bool(be) ) { char *c = (char*)&f; char tmp; tmp = c[0]; c[0] = c[3]; c[3] = tmp; tmp = c[1]; c[1] = c[2]; c[2] = tmp; } return copy_string((char *)&f,4); } /** double_bytes : number -> bigendian:bool -> string Returns the 8 bytes representation of the number as an IEEE 64-bit float **/ static value double_bytes( value n, value be ) { double f; val_check(n,number); val_check(be,bool); f = (double)val_number(n); if( neko_is_big_endian() != val_bool(be) ) { char *c = (char*)&f; char tmp; tmp = c[0]; c[0] = c[7]; c[7] = tmp; tmp = c[1]; c[1] = c[6]; c[6] = tmp; tmp = c[2]; c[2] = c[5]; c[5] = tmp; tmp = c[3]; c[3] = c[4]; c[4] = tmp; } return copy_string((char*)&f,8); } /** float_of_bytes : string -> bigendian:bool -> float Returns a float from a 4 bytes IEEE 32-bit representation **/ static value float_of_bytes( value s, value be ) { float f; val_check(s,string); val_check(be,bool); if( val_strlen(s) != 4 ) neko_error(); f = *(float*)val_string(s); if( neko_is_big_endian() != val_bool(be) ) { char *c = (char*)&f; char tmp; tmp = c[0]; c[0] = c[3]; c[3] = tmp; tmp = c[1]; c[1] = c[2]; c[2] = tmp; } return alloc_float(f); } /** double_of_bytes : string -> bigendian:bool -> float Returns a float from a 8 bytes IEEE 64-bit representation **/ static value double_of_bytes( value s, value be ) { double f; val_check(s,string); val_check(be,bool); if( val_strlen(s) != 8 ) neko_error(); f = *(double*)val_string(s); if( neko_is_big_endian() != val_bool(be) ) { char *c = (char*)&f; char tmp; tmp = c[0]; c[0] = c[7]; c[7] = tmp; tmp = c[1]; c[1] = c[6]; c[6] = tmp; tmp = c[2]; c[2] = c[5]; c[5] = tmp; tmp = c[3]; c[3] = c[4]; c[4] = tmp; } return alloc_float(f); } /** run_gc : major:bool -> void Run the Neko garbage collector **/ static value run_gc( value b ) { val_check(b,bool); if( val_bool(b) ) neko_gc_major(); else neko_gc_loop(); return val_null; } /** gc_stats : void -> { heap => int, free => int } Return the size of the GC heap and the among of free space, in bytes **/ static value gc_stats() { int heap, free; value o; neko_gc_stats(&heap,&free); o = alloc_object(NULL); alloc_field(o,val_id("heap"),alloc_int(heap)); alloc_field(o,val_id("free"),alloc_int(free)); return o; } /** enable_jit : ?bool -> ?bool Enable or disable the JIT. Calling enable_jit(null) tells if JIT is enabled or not **/ static value enable_jit( value b ) { if( val_is_null(b) ) return alloc_bool(neko_vm_jit(neko_vm_current(),-1)); val_check(b,bool); neko_vm_jit(neko_vm_current(),val_bool(b)); return val_null; } /** test : void -> void The test function, to check that library is reachable and correctly linked **/ static value test() { val_print(alloc_string("Calling a function inside std library...\n")); return val_null; } /** print_redirect : function:1? -> void Set a redirection function for all printed values. Setting it to null will cancel the redirection and restore previous printer. **/ static void print_callback( const char *s, int size, void *f ) { val_call1(f,copy_string(s,size)); } static value print_redirect( value f ) { neko_vm *vm = neko_vm_current(); if( val_is_null(f) ) { neko_vm_redirect(vm,NULL,NULL); return val_null; } val_check_function(f,1); neko_vm_redirect(vm,print_callback,f); return val_null; } /** set_trusted : bool -> void Change the trusted mode of the VM. This can optimize some operations such as module loading by turning off some checks. **/ static value set_trusted( value b ) { val_check(b,bool); neko_vm_trusted(neko_vm_current(),val_bool(b)); return val_null; } /** same_closure : any -> any -> bool Compare two functions by checking that they refer to the same implementation and that their environments contains physically equal values. **/ static value same_closure( value _f1, value _f2 ) { vfunction *f1 = (vfunction*)_f1; vfunction *f2 = (vfunction*)_f2; int i; if( !val_is_function(f1) || !val_is_function(f2) ) return val_false; if( f1 == f2 ) return val_true; if( f1->nargs != f2->nargs || f1->addr != f2->addr || f1->module != f2->module || val_array_size(f1->env) != val_array_size(f2->env) ) return val_false; for(i=0;ienv);i++) if( val_array_ptr(f1->env)[i] != val_array_ptr(f2->env)[i] ) return val_false; return val_true; } // ------------- MERGE SORT HELPERS ----------------------------- typedef struct { value *arr; value cmp; } m_sort; static int ms_compare( m_sort *m, int a, int b ) { value v = val_call2(m->cmp,m->arr[a],m->arr[b]); if( !val_is_int(v) ) return -1; return val_int(v); } static void ms_swap( m_sort *m, int a, int b ) { value tmp = m->arr[a]; m->arr[a] = m->arr[b]; m->arr[b] = tmp; } static int ms_lower( m_sort *m, int from, int to, int val ) { int len = to - from, half, mid; while( len > 0 ) { half = len>>1; mid = from + half; if( ms_compare(m, mid, val) < 0 ) { from = mid+1; len = len - half -1; } else len = half; } return from; } static int ms_upper( m_sort *m, int from, int to, int val ) { int len = to - from, half, mid; while( len > 0 ) { half = len>>1; mid = from + half; if( ms_compare(m, val, mid) < 0 ) len = half; else { from = mid+1; len = len - half -1; } } return from; } static int ms_gcd( int m, int n ) { while( n != 0 ) { int t = m % n; m=n; n=t; } return m; } static void ms_rotate( m_sort *m, int from, int mid, int to ) { int n; if( from==mid || mid==to ) return; n = ms_gcd(to - from, mid - from); while (n-- != 0) { value val = m->arr[from+n]; int shift = mid - from; int p1 = from+n, p2=from+n+shift; while (p2 != from + n) { m->arr[p1] = m->arr[p2]; p1=p2; if( to - p2 > shift) p2 += shift; else p2=from + (shift - (to - p2)); } m->arr[p1] = val; } } static void ms_do_merge( m_sort *m, int from, int pivot, int to, int len1, int len2 ) { int first_cut, second_cut, len11, len22, new_mid; if( len1 == 0 || len2==0 ) return; if( len1+len2 == 2 ) { if( ms_compare(m, pivot, from) < 0 ) ms_swap(m, pivot, from); return; } if (len1 > len2) { len11=len1>>1; first_cut = from + len11; second_cut = ms_lower(m, pivot, to, first_cut); len22 = second_cut - pivot; } else { len22 = len2>>1; second_cut = pivot + len22; first_cut = ms_upper(m, from, pivot, second_cut); len11=first_cut - from; } ms_rotate(m, first_cut, pivot, second_cut); new_mid=first_cut+len22; ms_do_merge(m, from, first_cut, new_mid, len11, len22); ms_do_merge(m, new_mid, second_cut, to, len1 - len11, len2 - len22); } static void merge_sort_rec( m_sort *m, int from, int to ) { int middle; if( to - from < 12 ) { // insert sort int i; if( to <= from ) return; for(i=from+1;i from ) { if( ms_compare(m,j,j-1) < 0 ) ms_swap(m,j-1,j); else break; j--; } } return; } middle = (from + to)>>1; merge_sort_rec(m, from, middle); merge_sort_rec(m, middle, to); ms_do_merge(m, from, middle, to, middle-from, to - middle); } /** merge_sort : array -> length:int -> cmp:function:2 -> void Sort the array using stable in-place merge sort and the [cmp] compare function. **/ static value merge_sort( value arr, value len, value cmp ) { m_sort m; val_check(arr,array); val_check(len,int); val_check_function(cmp,2); m.arr = val_array_ptr(arr); m.cmp = cmp; merge_sort_rec(&m,0,val_int(len)); return val_null; } DEFINE_PRIM(float_bytes,2); DEFINE_PRIM(double_bytes,2); DEFINE_PRIM(float_of_bytes,2); DEFINE_PRIM(double_of_bytes,2); DEFINE_PRIM(run_gc,1); DEFINE_PRIM(gc_stats,0); DEFINE_PRIM(enable_jit,1); DEFINE_PRIM(test,0); DEFINE_PRIM(print_redirect,1); DEFINE_PRIM(set_trusted,1); DEFINE_PRIM(same_closure,2); DEFINE_PRIM(merge_sort,3); /* ************************************************************************ */ neko-2-4-0/libs/std/module.c000066400000000000000000000151561464615675700156600ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #define READ_BUFSIZE 64 /**

Module

An API for reflexion of Neko bytecode modules.

**/ static int read_proxy( readp p, void *buf, int size ) { value fread = val_array_ptr(p)[0]; value vbuf = val_array_ptr(p)[1]; value ret; int len; if( size < 0 ) return -1; if( size > READ_BUFSIZE ) vbuf = alloc_empty_string(size); ret = val_call3(fread,vbuf,alloc_int(0),alloc_int(size)); if( !val_is_int(ret) ) return -1; len = val_int(ret); if( len < 0 || len > size ) return -1; memcpy(buf,val_string(vbuf),len); return len; } /** module_read : fread:(buf:string -> pos:int -> len:int -> int) -> loader:object -> 'module Read a module using the specified read function and the specified loader. **/ static value module_read( value fread, value loader ) { value p; neko_module *m; val_check_function(fread,3); val_check(loader,object); p = alloc_array(2); val_array_ptr(p)[0] = fread; val_array_ptr(p)[1] = alloc_empty_string(READ_BUFSIZE); m = neko_read_module(read_proxy,p,loader); if( m == NULL ) neko_error(); m->name = alloc_string(""); return alloc_abstract(neko_kind_module,m); } /** module_read_string : string -> loader:object -> 'module Read a module using the specified string datas. **/ static value module_read_string( value str, value loader ) { neko_module *m; string_pos p; val_check(str,string); val_check(loader,object); p.p = val_string(str); p.len = val_strlen(str); m = neko_read_module(neko_string_reader,&p,loader); if( m == NULL ) neko_error(); m->name = alloc_string(""); return alloc_abstract(neko_kind_module,m); } /** module_read_path : string list -> name:string -> loader:object -> 'module Read a module using the specified search path. **/ static value module_read_path( value path, value name, value loader ) { FILE *f; value fname; char *mname, *ext; neko_module *m; val_check(name,string); val_check(loader,object); mname = val_string(name); ext = strrchr(mname,'.'); if( ext && ext[1] == 'n' && ext[2] == 0 ) fname = neko_select_file(path,mname,""); else fname = neko_select_file(path,mname,".n"); f = fopen(val_string(fname),"rb"); if( f == NULL ) { buffer b = alloc_buffer("Module not found : "); buffer_append(b,mname); bfailure(b); } m = neko_read_module(neko_file_reader,f,loader); fclose(f); if( m == NULL ) { buffer b = alloc_buffer("Invalid module : "); val_buffer(b,name); bfailure(b); } m->name = alloc_string(val_string(name)); return alloc_abstract(neko_kind_module,m); } /** module_exec : 'module -> any Execute the module, return the calculated value **/ static value module_exec( value mv ) { neko_module *m; val_check_kind(mv,neko_kind_module); m = (neko_module*)val_data(mv); return neko_vm_execute(neko_vm_current(),m); } /** module_name : 'module -> string Return the module name **/ static value module_name( value mv ) { neko_module *m; val_check_kind(mv,neko_kind_module); m = (neko_module*)val_data(mv); return m->name; } /** module_set_name : 'module -> string -> void Set the module name **/ static value module_set_name( value mv, value str ) { neko_module *m; val_check_kind(mv,neko_kind_module); val_check(str,string); m = (neko_module*)val_data(mv); m->name = str; return val_null; } /** module_exports : 'module -> object Return the module export table **/ static value module_exports( value mv ) { neko_module *m; val_check_kind(mv,neko_kind_module); m = (neko_module*)val_data(mv); return m->exports; } /** module_loader : 'module -> object Return the module loader **/ static value module_loader( value mv ) { neko_module *m; val_check_kind(mv,neko_kind_module); m = (neko_module*)val_data(mv); return m->loader; } /** module_nglobals : 'module -> int Return the number of globals for this module **/ static value module_nglobals( value mv ) { neko_module *m; val_check_kind(mv,neko_kind_module); m = (neko_module*)val_data(mv); return alloc_int(m->nglobals); } /** module_global_get : 'module -> n:int -> any Get the [n]th global **/ static value module_global_get( value mv, value p ) { neko_module *m; unsigned int pp; val_check_kind(mv,neko_kind_module); m = (neko_module*)val_data(mv); val_check(p,int); pp = (unsigned)val_int(p); if( pp >= m->nglobals ) neko_error(); return m->globals[pp]; } /** module_global_set : 'module -> n:int -> any -> void Set the [n]th global **/ static value module_global_set( value mv, value p, value v ) { neko_module *m; unsigned int pp; val_check_kind(mv,neko_kind_module); m = (neko_module*)val_data(mv); val_check(p,int); pp = (unsigned)val_int(p); if( pp >= m->nglobals ) neko_error(); m->globals[pp] = v; return v; } /** module_code_size : 'module -> int return the codesize of the module **/ static value module_code_size( value mv ) { val_check_kind(mv,neko_kind_module); return alloc_int( ((neko_module*)val_data(mv))->codesize ); } DEFINE_PRIM(module_read,2); DEFINE_PRIM(module_read_string,2); DEFINE_PRIM(module_read_path,3); DEFINE_PRIM(module_exec,1); DEFINE_PRIM(module_name,1); DEFINE_PRIM(module_exports,1); DEFINE_PRIM(module_loader,1); DEFINE_PRIM(module_nglobals,1); DEFINE_PRIM(module_global_get,2); DEFINE_PRIM(module_global_set,3); DEFINE_PRIM(module_code_size,1); DEFINE_PRIM(module_set_name,2); /* ************************************************************************ */ neko-2-4-0/libs/std/process.c000066400000000000000000000267021464615675700160500ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #ifdef NEKO_WINDOWS # include #else # include # include # include # include # if !defined(NEKO_MAC) # if defined(NEKO_BSD) # include # else # include # endif # endif #endif #include #include typedef struct { #ifdef NEKO_WINDOWS HANDLE oread; HANDLE eread; HANDLE iwrite; PROCESS_INFORMATION pinf; #else int oread; int eread; int iwrite; int pid; #endif } vprocess; DEFINE_KIND(k_process); #define val_process(v) ((vprocess*)val_data(v)) /**

Process

An API for starting and communication with sub processes.

**/ #ifndef NEKO_WINDOWS static int do_close( int fd ) { POSIX_LABEL(close_again); if( close(fd) != 0 ) { HANDLE_EINTR(close_again); return 1; } return 0; } #endif static void free_process( value vp ) { vprocess *p = val_process(vp); # ifdef NEKO_WINDOWS CloseHandle(p->eread); CloseHandle(p->oread); CloseHandle(p->iwrite); CloseHandle(p->pinf.hProcess); CloseHandle(p->pinf.hThread); # else do_close(p->eread); do_close(p->oread); do_close(p->iwrite); # endif } /** process_run : cmd:string -> args:string array -> 'process Start a process using a command and the specified arguments. When args is not null, cmd and args will be auto-quoted/escaped. If no auto-quoting/escaping is desired, you should append necessary arguments to cmd as if it is inputted to the shell directly, and pass null as args. **/ static value process_run( value cmd, value vargs ) { int i, isRaw; vprocess *p; val_check(cmd,string); isRaw = val_is_null(vargs); if (!isRaw) { val_check(vargs,array); } # ifdef NEKO_WINDOWS { SECURITY_ATTRIBUTES sattr; STARTUPINFO sinf; HANDLE proc = GetCurrentProcess(); HANDLE oread,eread,iwrite; // creates commandline buffer b = alloc_buffer(NULL); value sargs; if (isRaw) { char* cmdexe = getenv("COMSPEC"); if (!cmdexe) cmdexe = "cmd.exe"; buffer_append(b,"\""); buffer_append(b,cmdexe); buffer_append(b,"\" /C \""); buffer_append(b,val_string(cmd)); buffer_append_char(b,'"'); } else { buffer_append_char(b,'"'); val_buffer(b,cmd); buffer_append_char(b,'"'); for(i=0;ioread,0,FALSE,DUPLICATE_SAME_ACCESS); DuplicateHandle(proc,eread,proc,&p->eread,0,FALSE,DUPLICATE_SAME_ACCESS); DuplicateHandle(proc,iwrite,proc,&p->iwrite,0,FALSE,DUPLICATE_SAME_ACCESS); CloseHandle(oread); CloseHandle(eread); CloseHandle(iwrite); if( !CreateProcess(NULL,val_string(sargs),NULL,NULL,TRUE,0,NULL,NULL,&sinf,&p->pinf) ) neko_error(); // close unused pipes CloseHandle(sinf.hStdOutput); CloseHandle(sinf.hStdError); CloseHandle(sinf.hStdInput); } # else char **argv; if (isRaw) { argv = (char**)alloc_private(sizeof(char*)*4); argv[0] = "/bin/sh"; argv[1] = "-c"; argv[2] = val_string(cmd); argv[3] = NULL; } else { argv = (char**)alloc_private(sizeof(char*)*(val_array_size(vargs)+2)); argv[0] = val_string(cmd); for(i=0;ipid = fork(); if( p->pid == -1 ) { do_close(input[0]); do_close(input[1]); do_close(output[0]); do_close(output[1]); do_close(error[0]); do_close(error[1]); neko_error(); } // child if( p->pid == 0 ) { close(input[1]); close(output[0]); close(error[0]); dup2(input[0],0); dup2(output[1],1); dup2(error[1],2); execvp(argv[0],argv); fprintf(stderr,"Command not found : %s\n",val_string(cmd)); exit(1); } // parent do_close(input[0]); do_close(output[1]); do_close(error[1]); p->iwrite = input[1]; p->oread = output[0]; p->eread = error[0]; # endif { value vp = alloc_abstract(k_process,p); val_gc(vp,free_process); return vp; } } #define CHECK_ARGS() \ vprocess *p; \ val_check_kind(vp,k_process); \ val_check(str,string); \ val_check(pos,int); \ val_check(len,int); \ if( val_int(pos) < 0 || val_int(len) < 0 || val_int(pos) + val_int(len) > val_strlen(str) ) \ neko_error(); \ p = val_process(vp); \ /** process_stdout_read : 'process -> buf:string -> pos:int -> len:int -> int Read up to [len] bytes in [buf] starting at [pos] from the process stdout. Returns the number of bytes readed this way. Raise an exception if this process stdout is closed and no more data is available for reading. **/ static value process_stdout_read( value vp, value str, value pos, value len ) { CHECK_ARGS(); # ifdef NEKO_WINDOWS { DWORD nbytes; if( !ReadFile(p->oread,val_string(str)+val_int(pos),val_int(len),&nbytes,NULL) ) neko_error(); return alloc_int(nbytes); } # else int nbytes; POSIX_LABEL(stdout_read_again); nbytes = read(p->oread,val_string(str)+val_int(pos),val_int(len)); if( nbytes < 0 ) { HANDLE_EINTR(stdout_read_again); neko_error(); } if( nbytes == 0 ) neko_error(); return alloc_int(nbytes); # endif } /** process_stderr_read : 'process -> buf:string -> pos:int -> len:int -> int Read up to [len] bytes in [buf] starting at [pos] from the process stderr. Returns the number of bytes readed this way. Raise an exception if this process stderr is closed and no more data is available for reading. **/ static value process_stderr_read( value vp, value str, value pos, value len ) { CHECK_ARGS(); # ifdef NEKO_WINDOWS { DWORD nbytes; if( !ReadFile(p->eread,val_string(str)+val_int(pos),val_int(len),&nbytes,NULL) ) neko_error(); return alloc_int(nbytes); } # else int nbytes; POSIX_LABEL(stderr_read_again); nbytes = read(p->eread,val_string(str)+val_int(pos),val_int(len)); if( nbytes < 0 ) { HANDLE_EINTR(stderr_read_again); neko_error(); } if( nbytes == 0 ) neko_error(); return alloc_int(nbytes); # endif } /** process_stdin_write : 'process -> buf:string -> pos:int -> len:int -> int Write up to [len] bytes from [buf] starting at [pos] to the process stdin. Returns the number of bytes writen this way. Raise an exception if this process stdin is closed. **/ static value process_stdin_write( value vp, value str, value pos, value len ) { CHECK_ARGS(); # ifdef NEKO_WINDOWS { DWORD nbytes; if( !WriteFile(p->iwrite,val_string(str)+val_int(pos),val_int(len),&nbytes,NULL) ) neko_error(); return alloc_int(nbytes); } # else int nbytes; POSIX_LABEL(stdin_write_again); nbytes = write(p->iwrite,val_string(str)+val_int(pos),val_int(len)); if( nbytes == -1 ) { HANDLE_EINTR(stdin_write_again); neko_error(); } return alloc_int(nbytes); # endif } /** process_stdin_close : 'process -> void Close the process standard input. **/ static value process_stdin_close( value vp ) { vprocess *p; val_check_kind(vp,k_process); p = val_process(vp); # ifdef NEKO_WINDOWS if( !CloseHandle(p->iwrite) ) neko_error(); # else if( do_close(p->iwrite) ) neko_error(); p->iwrite = -1; # endif return val_null; } /** process_exit : 'process -> int Wait until the process terminate, then returns its exit code. **/ static value process_exit( value vp ) { vprocess *p; val_check_kind(vp,k_process); p = val_process(vp); # ifdef NEKO_WINDOWS { DWORD rval; WaitForSingleObject(p->pinf.hProcess,INFINITE); if( !GetExitCodeProcess(p->pinf.hProcess,&rval) ) neko_error(); return alloc_int(rval); } # else int rval; while( waitpid(p->pid,&rval,0) != p->pid ) { if( errno == EINTR ) continue; neko_error(); } if( !WIFEXITED(rval) ) { if (WIFSIGNALED(rval)) { char msg[30]; sprintf(msg, "process killed by signal %d", WTERMSIG(rval)); val_throw(alloc_string(msg)); } else { neko_error(); } } return alloc_int(WEXITSTATUS(rval)); # endif } /** process_pid : 'process -> int Returns the process id. **/ static value process_pid( value vp ) { vprocess *p; val_check_kind(vp,k_process); p = val_process(vp); # ifdef NEKO_WINDOWS return alloc_int(p->pinf.dwProcessId); # else return alloc_int(p->pid); # endif } /** process_close : 'process -> void Close the process I/O. **/ static value process_close( value vp ) { val_check_kind(vp,k_process); free_process(vp); val_kind(vp) = NULL; val_gc(vp,NULL); return val_null; } /** process_kill : 'process -> void Terminates a running process. **/ static value process_kill( value vp ) { val_check_kind(vp,k_process); # ifdef NEKO_WINDOWS TerminateProcess(val_process(vp)->pinf.hProcess,-1); # else kill(val_process(vp)->pid,9); # endif return val_null; } DEFINE_PRIM(process_run,2); DEFINE_PRIM(process_stdout_read,4); DEFINE_PRIM(process_stderr_read,4); DEFINE_PRIM(process_stdin_close,1); DEFINE_PRIM(process_stdin_write,4); DEFINE_PRIM(process_exit,1); DEFINE_PRIM(process_pid,1); DEFINE_PRIM(process_close,1); DEFINE_PRIM(process_kill,1); /* ************************************************************************ */ neko-2-4-0/libs/std/random.c000066400000000000000000000103311464615675700156410ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #ifdef NEKO_WINDOWS # include # include #else # include # include # include #endif /**

Random

A seeded pseudo-random generator

**/ DEFINE_KIND(k_random); #define val_rnd(o) ((rnd*)val_data(o)) #define NSEEDS 25 #define MAX 7 typedef struct _rnd rnd; struct _rnd { unsigned long seeds[NSEEDS]; unsigned long cur; }; static unsigned long mag01[2]={ 0x0, 0x8ebfd028 // magic, don't change }; static const unsigned long init_seeds[] = { 0x95f24dab, 0x0b685215, 0xe76ccae7, 0xaf3ec239, 0x715fad23, 0x24a590ad, 0x69e4b5ef, 0xbf456141, 0x96bc1b7b, 0xa7bdf825, 0xc1de75b7, 0x8858a9c9, 0x2da87693, 0xb657f9dd, 0xffdc8a9f, 0x8121da71, 0x8b823ecb, 0x885d05f5, 0x4e20cd47, 0x5a9ad5d9, 0x512c0c03, 0xea857ccd, 0x4cc1d30f, 0x8891a8a1, 0xa6b7aadb }; static int rnd_size() { return sizeof(rnd); } static void rnd_set_seed( rnd *r, int s ) { int i; r->cur = 0; memcpy(r->seeds,init_seeds,sizeof(init_seeds)); for(i=0;iseeds[i] ^= s; } static rnd *rnd_init( void *data ) { rnd *r = (rnd*)data; int pid = getpid(); unsigned int time; #ifdef NEKO_WINDOWS time = GetTickCount(); #else struct timeval t; gettimeofday(&t,NULL); time = t.tv_sec * 1000000 + t.tv_usec; #endif rnd_set_seed(r,time ^ (pid | (pid << 16))); return r; } static unsigned int rnd_int( rnd *r ) { unsigned int y; int pos = r->cur++; if( pos >= NSEEDS ) { int kk; for(kk=0;kkseeds[kk] = r->seeds[kk+MAX] ^ (r->seeds[kk] >> 1) ^ mag01[r->seeds[kk] % 2]; for(;kkseeds[kk] = r->seeds[kk+(MAX-NSEEDS)] ^ (r->seeds[kk] >> 1) ^ mag01[r->seeds[kk] % 2]; r->cur = 1; pos = 0; } y = r->seeds[pos]; y ^= (y << 7) & 0x2b5b2500; y ^= (y << 15) & 0xdb8b0000; y ^= (y >> 16); return y; } static double rnd_float( rnd *r ) { double big = 4294967296.0; return ((rnd_int(r) / big + rnd_int(r)) / big + rnd_int(r)) / big; } /** random_new : void -> 'random Create a new random with random seed **/ static value random_new() { return alloc_abstract( k_random, rnd_init(alloc_private(rnd_size())) ); } /** random_set_seed : 'random -> int -> void Set the generator seed **/ static value random_set_seed( value o, value v ) { val_check_kind(o,k_random); val_check(v,any_int); rnd_set_seed(val_rnd(o),val_any_int(v)); return val_true; } /** random_int : 'random -> max:int -> int Return a random integer modulo [max] **/ static value random_int( value o, value max ) { val_check_kind(o,k_random); val_check(max,int); if( val_int(max) <= 0 ) return alloc_int(0); return alloc_int( (rnd_int(val_rnd(o)) & 0x3FFFFFFF) % val_int(max) ); } /** random_float : 'random -> float Return a random float **/ static value random_float( value o ) { val_check_kind(o,k_random); return alloc_float( rnd_float(val_rnd(o)) ); } DEFINE_PRIM(random_new,0); DEFINE_PRIM(random_set_seed,2); DEFINE_PRIM(random_int,2); DEFINE_PRIM(random_float,1); /* ************************************************************************ */ neko-2-4-0/libs/std/serialize.c000066400000000000000000000327221464615675700163600ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #define BUF_SIZE 4096 #define ERROR() val_throw(alloc_string("Invalid serialized data")) typedef struct strlist { unsigned char *str; int slen; struct strlist *next; } strlist; typedef struct odatalist { int k; value data; struct odatalist *left; struct odatalist *right; } odatalist; typedef struct { odatalist *refs; int nrefs; value *trefs; int tsize; strlist *olds; unsigned char *cur; int size; int pos; int totlen; int nrec; } sbuffer; extern field id_module; extern field id_loadmodule; extern field id_loadprim; extern field id_serialize; extern field id_unserialize; static void buffer_alloc( sbuffer *b, int size ) { strlist *str = (strlist*)alloc(sizeof(strlist)); str->str = b->cur; str->slen = b->pos; str->next = b->olds; b->olds = str; b->totlen += b->pos; b->pos = 0; b->size = size; b->cur = (unsigned char*)alloc_private(size); } static void write_char( sbuffer *b, char c ) { if( b->pos == b->size ) buffer_alloc(b,BUF_SIZE); b->cur[b->pos++] = c; } static int read_char( sbuffer *b ) { if( b->pos >= b->size ) return -1; return b->cur[b->pos++]; } static void write_str( sbuffer *b, int len, const void *s ) { int left = b->size - b->pos; if( left == 0 ) { buffer_alloc(b,BUF_SIZE); left = b->size - b->pos; } if( left >= len ) { memcpy(b->cur + b->pos,s,len); b->pos += len; } else { memcpy(b->cur + b->pos,s,left); b->pos += left; write_str(b,len - left, (char*)s + left); } } static void read_str( sbuffer *b, int len, void *s ) { if( b->pos + len > b->size ) ERROR(); memcpy(s,b->cur + b->pos, len); b->pos += len; } static void write_int( sbuffer *b, int n ) { write_str(b,4,&n); } static int read_int( sbuffer *b ) { int n; if( b->pos + 4 > b->size ) ERROR(); memcpy(&n,b->cur + b->pos,4); b->pos += 4; return n; } static void lookup_serialize_field( value data, field id, void *v ) { if( id == id_serialize ) *(value*)v = data; } static bool write_ref( sbuffer *b, value o, value *serialize ) { odatalist *d = b->refs, *prev = NULL; while( d != NULL ) { if( d->data < o ) { prev = d; d = d->left; } else if( d->data == o ) { write_char(b,'r'); write_int(b,b->nrefs - 1 - d->k); return true; } else { prev = d; d = d->right; } } if( serialize != NULL ) { *serialize = NULL; val_iter_fields(o,lookup_serialize_field,serialize); if( *serialize != NULL ) return false; } d = (odatalist*)alloc(sizeof(odatalist)); d->data = o; d->k = b->nrefs++; d->left = NULL; d->right = NULL; if( prev == NULL ) b->refs = d; else if( prev->data < o ) prev->left = d; else prev->right = d; return false; } static void add_ref( sbuffer *b, value v ) { if( b->nrefs == b->tsize ) { int nsize = b->tsize?(b->tsize*2):16; value *ntrefs = (value*)alloc(sizeof(value) * nsize); memcpy(ntrefs,b->trefs,b->tsize * sizeof(value)); b->trefs = ntrefs; b->tsize = nsize; } b->trefs[b->nrefs++] = v; } static void serialize_fields_rec( value data, field id, void *b ); void serialize_rec( sbuffer *b, value o ) { b->nrec++; if( b->nrec > 350 ) failure("Serialization stack overflow"); switch( val_type(o) ) { case VAL_NULL: write_char(b,'N'); break; case VAL_BOOL: if( val_bool(o) ) write_char(b,'T'); else write_char(b,'F'); break; case VAL_INT: write_char(b,'i'); write_int(b,val_int(o)); break; case VAL_FLOAT: write_char(b,'f'); write_str(b,sizeof(tfloat),&val_float(o)); break; case VAL_STRING: if( !write_ref(b,o,NULL) ) { write_char(b,'s'); write_int(b,val_strlen(o)); write_str(b,val_strlen(o),val_string(o)); } break; case VAL_OBJECT: { value s; if( !write_ref(b,o,&s) ) { if( s != NULL ) { // reference was not written if( !val_is_function(s) || (val_fun_nargs(s) != 0 && val_fun_nargs(s) != VAR_ARGS) ) failure("Invalid __serialize method"); write_char(b,'x'); serialize_rec(b,((neko_module*)((vfunction*)s)->module)->name); serialize_rec(b,val_ocall0(o,id_serialize)); // put reference back write_ref(b,o,NULL); break; } write_char(b,'o'); val_iter_fields(o,serialize_fields_rec,b); write_int(b,0); o = (value)((vobject*)o)->proto; if( o == NULL ) write_char(b,'z'); else { write_char(b,'p'); serialize_rec(b,o); } } } break; case VAL_ARRAY: if( !write_ref(b,o,NULL) ) { int i; int n = val_array_size(o); write_char(b,'a'); write_int(b,n); for(i=0;ienv != alloc_array(0) ) failure("Cannot Serialize Primitive with environment"); write_char(b,'p'); write_int(b,((vfunction*)o)->nargs); serialize_rec(b,((vfunction*)o)->module); break; } if( val_tag(o) == VAL_JITFUN ) failure("Cannot Serialize JIT method"); write_char(b,'L'); m = (neko_module*)((vfunction*)o)->module; serialize_rec(b,m->name); write_int(b,(int)((int_val*)((vfunction*)o)->addr - m->code)); write_int(b,((vfunction*)o)->nargs); serialize_rec(b,((vfunction*)o)->env); } break; case VAL_INT32: write_char(b,'I'); write_int(b,val_int32(o)); break; case VAL_ABSTRACT: if( val_is_kind(o,k_hash) ) { int i; vhash *h = val_hdata(o); write_char(b,'h'); write_int(b,h->ncells); write_int(b,h->nitems); for(i=0;incells;i++) { hcell *c = h->cells[i]; while( c != NULL ) { write_int(b,c->hkey); serialize_rec(b,c->key); serialize_rec(b,c->val); c = c->next; } } break; } default: failure("Cannot Serialize Abstract"); break; } b->nrec--; } static void serialize_fields_rec( value data, field id, void *b ) { write_int(b,(int)id); serialize_rec(b,data); } /**

Serialize

Serialization can be used in order to store permanantly some runtime value. Serialization of all values is possible, except Abstracts, with the special cases of ['int32] and ['hash] which are handled as specific cases.

Serialization of bytecode function is possible, but will result in a runtime exception when deserializing if the function offset in the bytecode has changed.

You can define the __serialize method of an object. When this method is found when serializing the object, it is called with no arguments and its return value will be serialized. The name of the module the method is declared in will also be serialized. When unserializing, the module is loaded and its __unserialize exported function is called with the value that was returned by __serialize.

**/ /** serialize : any -> string Serialize any value recursively **/ static value serialize( value o ) { sbuffer b; value v; char *s; strlist *l; b.olds = NULL; b.refs = NULL; b.nrefs = 0; b.cur = (unsigned char*)alloc_private(BUF_SIZE); b.size = BUF_SIZE; b.pos = 0; b.totlen = 0; b.nrec = 0; serialize_rec(&b,o); v = alloc_empty_string(b.pos + b.totlen); s = (char*)val_string(v); s += b.totlen; l = b.olds; memcpy(s,b.cur,b.pos); while( l != NULL ) { s -= l->slen; memcpy(s,l->str,l->slen); l = l->next; } return v; } static value unserialize_rec( sbuffer *b, value loader ) { switch( read_char(b) ) { case 'N': return val_null; case 'T': return val_true; case 'F': return val_false; case 'i': return alloc_int(read_int(b)); case 'I': return alloc_int32(read_int(b)); case 'f': { tfloat d; read_str(b,sizeof(tfloat),&d); return alloc_float(d); } case 's': { int l = read_int(b); value v; if( l < 0 || l > max_string_size ) ERROR(); v = alloc_empty_string(l); add_ref(b,v); read_str(b,l,(char*)val_string(v)); return v; } case 'o': { int f; value o = alloc_object(NULL); add_ref(b,o); while( (f = read_int(b)) != 0 ) { value fval = unserialize_rec(b,loader); alloc_field(o,(field)f,fval); } switch( read_char(b) ) { case 'p': { value v = unserialize_rec(b,loader); if( !val_is_object(v) ) ERROR(); ((vobject*)o)->proto = (vobject*)v; } break; case 'z': break; default: ERROR(); } return o; } case 'r': { int n = read_int(b); if( n < 0 || n >= b->nrefs ) ERROR(); return b->trefs[b->nrefs - n - 1]; } case 'a': { int i; int n = read_int(b); value o; value *t; if( n < 0 || n > max_array_size ) ERROR(); o = alloc_array(n); t = val_array_ptr(o); add_ref(b,o); for(i=0;it = f2->t; f->addr = f2->addr; f->module = f2->module; return (value)f; } case 'L': { vfunction *f = (vfunction*)alloc_function((void*)1,0,NULL); value mname; int pos; int nargs; value env; add_ref(b,(value)f); mname = unserialize_rec(b,loader); pos = read_int(b); nargs = read_int(b); env = unserialize_rec(b,loader); if( !val_is_array(env) ) ERROR(); { value exp = val_ocall2(loader,id_loadmodule,mname,loader); value mval; unsigned int i; int_val *mpos; neko_module *m; if( !val_is_object(exp) ) { buffer b = alloc_buffer("module "); val_buffer(b,mname); buffer_append(b," is not an object"); bfailure(b); } mval = val_field(exp,id_module); if( !val_is_kind(mval,neko_kind_module) ) { buffer b = alloc_buffer("module "); val_buffer(b,mname); buffer_append(b," has invalid type"); bfailure(b); } m = (neko_module*)val_data(mval); mpos = m->code + pos; for(i=0;inglobals;i++) { vfunction *g = (vfunction*)m->globals[i]; if( val_is_function(g) && g->addr == mpos && g->module == m && g->nargs == nargs ) { f->t = VAL_FUNCTION; f->env = env; f->addr = mpos; f->nargs = nargs; f->module = m; return (value)f; } } { buffer b = alloc_buffer("module "); val_buffer(b,mname); buffer_append(b," has been modified"); bfailure(b); } } return val_null; } case 'x': { value mname = unserialize_rec(b,loader); value data = unserialize_rec(b,loader); value exports = val_ocall2(loader,id_loadmodule,mname,loader); value s; if( !val_is_object(exports) ) { buffer b = alloc_buffer("module "); val_buffer(b,mname); buffer_append(b," is not an object"); bfailure(b); } s = val_field(exports,id_unserialize); if( !val_is_function(s) || (val_fun_nargs(s) != 1 && val_fun_nargs(s) != VAR_ARGS) ) { buffer b = alloc_buffer("module "); val_buffer(b,mname); buffer_append(b," has invalid __unserialize function"); } s = val_call1(s,data); add_ref(b,s); return s; } case 'h': { int i; vhash *h = (vhash*)alloc(sizeof(vhash)); h->ncells = read_int(b); h->nitems = read_int(b); h->cells = (hcell**)alloc(sizeof(hcell*)*h->ncells); for(i=0;incells;i++) h->cells[i] = NULL; for(i=0;initems;i++) { hcell **p; hcell *c = (hcell*)alloc(sizeof(hcell)); c->hkey = read_int(b); c->key = unserialize_rec(b,loader); c->val = unserialize_rec(b,loader); c->next = NULL; p = &h->cells[c->hkey % h->ncells]; while( *p != NULL ) p = &(*p)->next; *p = c; } return alloc_abstract(k_hash,h); } default: ERROR(); return val_null; } } /** unserialize : string -> #loader -> any Unserialize a stored value. Need a loader to look for modules if some bytecode functions have been serialized. **/ static value unserialize( value s, value loader ) { sbuffer b; val_check(s,string); b.cur = (unsigned char*)val_string(s); b.pos = 0; b.olds = NULL; b.trefs = NULL; b.tsize = 0; b.nrefs = 0; b.size = val_strlen(s); b.totlen = 0; return unserialize_rec(&b,loader); } DEFINE_PRIM(serialize,1); DEFINE_PRIM(unserialize,2); /* ************************************************************************ */ neko-2-4-0/libs/std/socket.c000066400000000000000000001000321464615675700156470ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #ifdef NEKO_WINDOWS # include # include # define FDSIZE(n) (sizeof(u_int) + (n) * sizeof(SOCKET)) # define SHUT_WR SD_SEND # define SHUT_RD SD_RECEIVE # define SHUT_RDWR SD_BOTH static bool init_done = false; static WSADATA init_data; #else # include # include # include # include # include # include # include # include # include # include # include # include typedef int SOCKET; # define closesocket close # define SOCKET_ERROR (-1) # define INVALID_SOCKET (-1) #endif #ifdef NEKO_LINUX # include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) # include # define HAS_EPOLL #endif #endif #ifndef HAS_EPOLL # define EPOLLIN 0x001 # define EPOLLOUT 0x004 #endif #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 #endif #define NRETRYS 20 typedef struct { SOCKET sock; char *buf; int size; int ret; } sock_tmp; typedef struct { int max; # ifdef NEKO_WINDOWS struct fd_set *fdr; struct fd_set *fdw; struct fd_set *outr; struct fd_set *outw; # else struct pollfd *fds; int rcount; int wcount; # endif value ridx; value widx; } polldata; typedef struct { int maxevents; value result; #ifndef HAS_EPOLL value read; value write; int rcount; int wcount; #else int epollfd; struct epoll_event *events; #endif } epolldata; DEFINE_KIND(k_socket); DEFINE_KIND(k_poll); DEFINE_KIND(k_epoll); #define val_sock(o) ((SOCKET)(int_val)val_data(o)) #define val_poll(o) ((polldata*)val_data(o)) #define val_epoll(o) ((epolldata*)val_data(o)) static field f_host; static field f_port; /**

Socket

TCP and UDP sockets

**/ static value block_error() { #ifdef NEKO_WINDOWS int err = WSAGetLastError(); if( err == WSAEWOULDBLOCK || err == WSAEALREADY || err == WSAETIMEDOUT ) #else if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EALREADY ) #endif val_throw(alloc_string("Blocking")); neko_error(); return val_true; } /** socket_init : void -> void Initialize the socket API. Must be called at least once per process before using any socket or host function. **/ static value socket_init() { #ifdef NEKO_WINDOWS if( !init_done ) { WSAStartup(MAKEWORD(2,0),&init_data); init_done = true; } #endif f_host = val_id("host"); f_port = val_id("port"); return val_true; } /** socket_new : udp:bool -> 'socket Create a new socket, TCP or UDP **/ static value socket_new( value udp ) { SOCKET s; val_check(udp,bool); if( val_bool(udp) ) s = socket(AF_INET,SOCK_DGRAM,0); else s = socket(AF_INET,SOCK_STREAM,0); if( s == INVALID_SOCKET ) neko_error(); # ifdef NEKO_MAC setsockopt(s,SOL_SOCKET,SO_NOSIGPIPE,NULL,0); # endif # ifdef NEKO_POSIX // we don't want sockets to be inherited in case of exec { int old = fcntl(s,F_GETFD,0); if( old >= 0 ) fcntl(s,F_SETFD,old|FD_CLOEXEC); } # endif return alloc_abstract(k_socket,(value)(int_val)s); } /** socket_close : 'socket -> void Close a socket. Any subsequent operation on this socket will fail **/ static value socket_close( value o ) { val_check_kind(o,k_socket); POSIX_LABEL(close_again); if( closesocket(val_sock(o)) ) { HANDLE_EINTR(close_again); } val_kind(o) = NULL; return val_true; } /** socket_send_char : 'socket -> int -> void Send a character over a connected socket. Must be in the range 0..255 **/ static value socket_send_char( value o, value v ) { int c; unsigned char cc; val_check_kind(o,k_socket); val_check(v,int); c = val_int(v); if( c < 0 || c > 255 ) neko_error(); cc = (unsigned char)c; POSIX_LABEL(send_char_again); if( send(val_sock(o),&cc,1,MSG_NOSIGNAL) == SOCKET_ERROR ) { HANDLE_EINTR(send_char_again); return block_error(); } return val_true; } /** socket_send : 'socket -> buf:string -> pos:int -> len:int -> int Send up to [len] bytes from [buf] starting at [pos] over a connected socket. Return the number of bytes sent. **/ static value socket_send( value o, value data, value pos, value len ) { int p,l,dlen; val_check_kind(o,k_socket); val_check(data,string); val_check(pos,int); val_check(len,int); p = val_int(pos); l = val_int(len); dlen = val_strlen(data); if( p < 0 || l < 0 || p > dlen || p + l > dlen ) neko_error(); POSIX_LABEL(send_again); dlen = send(val_sock(o), val_string(data) + p , l, MSG_NOSIGNAL); if( dlen == SOCKET_ERROR ) { HANDLE_EINTR(send_again); return block_error(); } return alloc_int(dlen); } static void tmp_recv( void *_t ) { sock_tmp *t = (sock_tmp*)_t; t->ret = recv(t->sock,t->buf,t->size,MSG_NOSIGNAL); } /** socket_recv : 'socket -> buf:string -> pos:int -> len:int -> int Read up to [len] bytes from [buf] starting at [pos] from a connected socket. Return the number of bytes readed. **/ static value socket_recv( value o, value data, value pos, value len ) { int p,l,dlen,ret; int retry = 0; val_check_kind(o,k_socket); val_check(data,string); val_check(pos,int); val_check(len,int); p = val_int(pos); l = val_int(len); dlen = val_strlen(data); if( p < 0 || l < 0 || p > dlen || p + l > dlen ) neko_error(); POSIX_LABEL(recv_again); if( retry++ > NRETRYS ) { sock_tmp t; t.sock = val_sock(o); t.buf = val_string(data) + p; t.size = l; neko_thread_blocking(tmp_recv,&t); ret = t.ret; } else ret = recv(val_sock(o), val_string(data) + p , l, MSG_NOSIGNAL); if( ret == SOCKET_ERROR ) { HANDLE_EINTR(recv_again); return block_error(); } return alloc_int(ret); } /** socket_recv_char : 'socket -> int Read a single char from a connected socket. **/ static value socket_recv_char( value o ) { int ret; int retry = 0; unsigned char cc; val_check_kind(o,k_socket); POSIX_LABEL(recv_char_again); if( retry++ > NRETRYS ) { sock_tmp t; t.sock = val_sock(o); t.buf = (char*)&cc; t.size = 1; neko_thread_blocking(tmp_recv,&t); ret = t.ret; } else ret = recv(val_sock(o),&cc,1,MSG_NOSIGNAL); if( ret == SOCKET_ERROR ) { HANDLE_EINTR(recv_char_again); return block_error(); } if( ret == 0 ) neko_error(); return alloc_int(cc); } /** socket_write : 'socket -> string -> void Send the whole content of a string over a connected socket. **/ static value socket_write( value o, value data ) { const char *cdata; int datalen, slen; val_check_kind(o,k_socket); val_check(data,string); cdata = val_string(data); datalen = val_strlen(data); while( datalen > 0 ) { POSIX_LABEL(write_again); slen = send(val_sock(o),cdata,datalen,MSG_NOSIGNAL); if( slen == SOCKET_ERROR ) { HANDLE_EINTR(write_again); return block_error(); } cdata += slen; datalen -= slen; } return val_true; } /** socket_read : 'socket -> string Read the whole content of a the data available from a socket until the connection close. If the socket hasn't been close by the other side, the function might block. **/ static value socket_read( value o ) { buffer b; char buf[256]; int len; val_check_kind(o,k_socket); b = alloc_buffer(NULL); while( true ) { POSIX_LABEL(read_again); len = recv(val_sock(o),buf,256,MSG_NOSIGNAL); if( len == SOCKET_ERROR ) { HANDLE_EINTR(read_again); return block_error(); } if( len == 0 ) break; buffer_append_sub(b,buf,len); } return buffer_to_string(b); } /** host_resolve : string -> 'int32 Resolve the given host string into an IP address. **/ static value host_resolve( value host ) { unsigned int ip; val_check(host,string); ip = inet_addr(val_string(host)); if( ip == INADDR_NONE ) { struct hostent *h; # if defined(NEKO_WINDOWS) || defined(NEKO_MAC) || defined(NEKO_CYGWIN) h = gethostbyname(val_string(host)); # else struct hostent hbase; char buf[1024]; int errcode; gethostbyname_r(val_string(host),&hbase,buf,1024,&h,&errcode); # endif if( h == NULL ) neko_error(); ip = *((unsigned int*)h->h_addr); } return alloc_int32(ip); } /** host_to_string : 'int32 -> string Return a string representation of the IP address. **/ static value host_to_string( value ip ) { struct in_addr i; val_check(ip,int32); *(int*)&i = val_int32(ip); return alloc_string( inet_ntoa(i) ); } /** host_reverse : 'int32 -> string Reverse the DNS of the given IP address. **/ static value host_reverse( value host ) { struct hostent *h; unsigned int ip; val_check(host,int32); ip = val_int32(host); # if defined(NEKO_WINDOWS) || defined(NEKO_MAC) || defined(NEKO_CYGWIN) h = gethostbyaddr((char *)&ip,4,AF_INET); # else struct hostent htmp; int errcode; char buf[1024]; gethostbyaddr_r((char *)&ip,4,AF_INET,&htmp,buf,1024,&h,&errcode); # endif if( h == NULL ) neko_error(); return alloc_string( h->h_name ); } /** host_local : void -> string Return the local host name. **/ static value host_local() { char buf[256]; if( gethostname(buf,256) == SOCKET_ERROR ) neko_error(); return alloc_string(buf); } /** socket_connect : 'socket -> host:'int32 -> port:int -> void Connect the socket the given [host] and [port] **/ static value socket_connect( value o, value host, value port ) { struct sockaddr_in addr; val_check_kind(o,k_socket); val_check(host,int32); val_check(port,int); memset(&addr,0,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(val_int(port)); *(int*)&addr.sin_addr.s_addr = val_int32(host); if( connect(val_sock(o),(struct sockaddr*)&addr,sizeof(addr)) != 0 ) return block_error(); return val_true; } /** socket_listen : 'socket -> int -> void Listen for a number of connections **/ static value socket_listen( value o, value n ) { val_check_kind(o,k_socket); val_check(n,int); if( listen(val_sock(o),val_int(n)) == SOCKET_ERROR ) neko_error(); return val_true; } static fd_set INVALID; static fd_set *make_socket_array( value a, int len, fd_set *tmp, SOCKET *n ) { int i; SOCKET sock; if( val_is_null(a) ) return NULL; if( !val_is_array(a) ) return &INVALID; if( len > FD_SETSIZE ) val_throw(alloc_string("Too many sockets in select")); FD_ZERO(tmp); for(i=0;i *n ) *n = sock; FD_SET(sock,tmp); } return tmp; } static value make_array_result( value a, fd_set *tmp ) { value r; int i, len; int pos = 0; if( tmp == NULL ) return val_null; len = val_array_size(a); r = alloc_array(len); for(i=0;itv_usec = (int)((f - (int)f) * 1000000); t->tv_sec = (int)f; } /** socket_select : read : 'socket array -> write : 'socket array -> others : 'socket array -> timeout:number? -> 'socket array array Perform the [select] operation. Timeout is in seconds or [null] if infinite **/ static value socket_select( value rs, value ws, value es, value timeout ) { struct timeval tval; struct timeval *tt; SOCKET n = 0; fd_set rx, wx, ex; fd_set *ra, *wa, *ea; value r; POSIX_LABEL(select_again); ra = make_socket_array(rs,val_array_size(rs),&rx,&n); wa = make_socket_array(ws,val_array_size(ws),&wx,&n); ea = make_socket_array(es,val_array_size(es),&ex,&n); if( ra == &INVALID || wa == &INVALID || ea == &INVALID ) neko_error(); if( val_is_null(timeout) ) tt = NULL; else { val_check(timeout,number); tt = &tval; init_timeval(val_number(timeout),tt); } if( select((int)(n+1),ra,wa,ea,tt) == SOCKET_ERROR ) { HANDLE_EINTR(select_again); neko_error(); } r = alloc_array(3); val_array_ptr(r)[0] = make_array_result(rs,ra); val_array_ptr(r)[1] = make_array_result(ws,wa); val_array_ptr(r)[2] = make_array_result(es,ea); return r; } /** socket_bind : 'socket -> host : 'int32 -> port:int -> void Bind the socket for server usage on the given host and port **/ static value socket_bind( value o, value host, value port ) { int opt = 1; struct sockaddr_in addr; val_check_kind(o,k_socket); val_check(host,int32); val_check(port,int); memset(&addr,0,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(val_int(port)); *(int*)&addr.sin_addr.s_addr = val_int32(host); #ifndef NEKO_WINDOWS setsockopt(val_sock(o),SOL_SOCKET,SO_REUSEADDR,(char*)&opt,sizeof(opt)); #endif if( bind(val_sock(o),(struct sockaddr*)&addr,sizeof(addr)) == SOCKET_ERROR ) neko_error(); return val_true; } /** socket_accept : 'socket -> 'socket Accept an incoming connection request **/ static value socket_accept( value o ) { struct sockaddr_in addr; unsigned int addrlen = sizeof(addr); SOCKET s; val_check_kind(o,k_socket); POSIX_LABEL(accept_again); s = accept(val_sock(o),(struct sockaddr*)&addr,&addrlen); if( s == INVALID_SOCKET ) { HANDLE_EINTR(accept_again); return block_error(); } return alloc_abstract(k_socket,(value)(int_val)s); } /** socket_peer : 'socket -> #address Return the socket connected peer address composed of an (host,port) array **/ static value socket_peer( value o ) { struct sockaddr_in addr; unsigned int addrlen = sizeof(addr); value ret; val_check_kind(o,k_socket); if( getpeername(val_sock(o),(struct sockaddr*)&addr,&addrlen) == SOCKET_ERROR ) neko_error(); ret = alloc_array(2); val_array_ptr(ret)[0] = alloc_int32(*(int*)&addr.sin_addr); val_array_ptr(ret)[1] = alloc_int(ntohs(addr.sin_port)); return ret; } /** socket_host : 'socket -> #address Return the socket local address composed of an (host,port) array **/ static value socket_host( value o ) { struct sockaddr_in addr; unsigned int addrlen = sizeof(addr); value ret; val_check_kind(o,k_socket); if( getsockname(val_sock(o),(struct sockaddr*)&addr,&addrlen) == SOCKET_ERROR ) neko_error(); ret = alloc_array(2); val_array_ptr(ret)[0] = alloc_int32(*(int*)&addr.sin_addr); val_array_ptr(ret)[1] = alloc_int(ntohs(addr.sin_port)); return ret; } /** socket_set_timeout : 'socket -> timout:number? -> void Set the socket send and recv timeout in seconds to the given value (or null for blocking) **/ static value socket_set_timeout( value o, value t ) { #ifdef NEKO_WINDOWS int time; val_check_kind(o,k_socket); if( val_is_null(t) ) time = 0; else { val_check(t,number); time = (int)(val_number(t) * 1000); } #else struct timeval time; val_check_kind(o,k_socket); if( val_is_null(t) ) { time.tv_usec = 0; time.tv_sec = 0; } else { val_check(t,number); init_timeval(val_number(t),&time); } #endif if( setsockopt(val_sock(o),SOL_SOCKET,SO_SNDTIMEO,(char*)&time,sizeof(time)) != 0 ) neko_error(); if( setsockopt(val_sock(o),SOL_SOCKET,SO_RCVTIMEO,(char*)&time,sizeof(time)) != 0 ) neko_error(); return val_true; } /** socket_shutdown : 'socket -> read:bool -> write:bool -> void Prevent the socket from further reading or writing or both. **/ static value socket_shutdown( value o, value r, value w ) { val_check_kind(o,k_socket); val_check(r,bool); val_check(w,bool); if( !val_bool(r) && !val_bool(w) ) return val_true; if( shutdown(val_sock(o),val_bool(r)?(val_bool(w)?SHUT_RDWR:SHUT_RD):SHUT_WR) ) neko_error(); return val_true; } /** socket_set_blocking : 'socket -> bool -> void Turn on/off the socket blocking mode. **/ static value socket_set_blocking( value o, value b ) { val_check_kind(o,k_socket); val_check(b,bool); #ifdef NEKO_WINDOWS { unsigned long arg = val_bool(b)?0:1; if( ioctlsocket(val_sock(o),FIONBIO,&arg) != 0 ) neko_error(); } #else { int rights = fcntl(val_sock(o),F_GETFL); if( rights == -1 ) neko_error(); if( val_bool(b) ) rights &= ~O_NONBLOCK; else rights |= O_NONBLOCK; if( fcntl(val_sock(o),F_SETFL,rights) == -1 ) neko_error(); } #endif return val_true; } /** socket_poll_alloc : int -> 'poll Allocate memory to perform polling on a given number of sockets **/ static value socket_poll_alloc( value nsocks ) { polldata *p; int i; val_check(nsocks,int); p = (polldata*)alloc(sizeof(polldata)); p->max = val_int(nsocks); if( p->max < 0 || p->max > 1000000 ) neko_error(); # ifdef NEKO_WINDOWS { p->fdr = (fd_set*)alloc_private(FDSIZE(p->max)); p->fdw = (fd_set*)alloc_private(FDSIZE(p->max)); p->outr = (fd_set*)alloc_private(FDSIZE(p->max)); p->outw = (fd_set*)alloc_private(FDSIZE(p->max)); p->fdr->fd_count = 0; p->fdw->fd_count = 0; } # else p->fds = (struct pollfd*)alloc_private(sizeof(struct pollfd) * p->max); p->rcount = 0; p->wcount = 0; # endif p->ridx = alloc_array(p->max+1); p->widx = alloc_array(p->max+1); for(i=0;i<=p->max;i++) { val_array_ptr(p->ridx)[i] = alloc_int(-1); val_array_ptr(p->widx)[i] = alloc_int(-1); } return alloc_abstract(k_poll, p); } /** socket_poll_prepare : 'poll -> read:'socket array -> write:'socket array -> int array array Prepare a poll for scanning events on sets of sockets. **/ static value socket_poll_prepare( value pdata, value rsocks, value wsocks ) { polldata *p; int i,len; val_check(rsocks,array); val_check(wsocks,array); val_check_kind(pdata,k_poll); p = val_poll(pdata); len = val_array_size(rsocks); if( len + val_array_size(wsocks) > p->max ) val_throw(alloc_string("Too many sockets in poll")); # ifdef NEKO_WINDOWS for(i=0;ifdr->fd_array[i] = val_sock(s); } p->fdr->fd_count = len; len = val_array_size(wsocks); for(i=0;ifdw->fd_array[i] = val_sock(s); } p->fdw->fd_count = len; # else for(i=0;ifds[i].fd = val_sock(s); p->fds[i].events = POLLIN; p->fds[i].revents = 0; } p->rcount = len; len = val_array_size(wsocks); for(i=0;ircount; value s = val_array_ptr(wsocks)[i]; val_check_kind(s,k_socket); p->fds[k].fd = val_sock(s); p->fds[k].events = POLLOUT; p->fds[k].revents = 0; } p->wcount = len; # endif { value a = alloc_array(2); val_array_ptr(a)[0] = p->ridx; val_array_ptr(a)[1] = p->widx; return a; } } /** socket_poll_events : 'poll -> timeout:float -> void Update the read/write flags arrays that were created with [socket_poll_prepare]. **/ static value socket_poll_events( value pdata, value timeout ) { polldata *p; # ifdef NEKO_WINDOWS unsigned int i; int k = 0; struct timeval t; val_check_kind(pdata,k_poll); p = val_poll(pdata); memcpy(p->outr,p->fdr,FDSIZE(p->fdr->fd_count)); memcpy(p->outw,p->fdw,FDSIZE(p->fdw->fd_count)); val_check(timeout,number); init_timeval(val_number(timeout),&t); if( p->fdr->fd_count + p->fdw->fd_count != 0 && select(0,p->outr,p->outw,NULL,&t) == SOCKET_ERROR ) neko_error(); k = 0; for(i=0;ifdr->fd_count;i++) if( FD_ISSET(p->fdr->fd_array[i],p->outr) ) val_array_ptr(p->ridx)[k++] = alloc_int(i); val_array_ptr(p->ridx)[k] = alloc_int(-1); k = 0; for(i=0;ifdw->fd_count;i++) if( FD_ISSET(p->fdw->fd_array[i],p->outw) ) val_array_ptr(p->widx)[k++] = alloc_int(i); val_array_ptr(p->widx)[k] = alloc_int(-1); #else int i,k; int tot; val_check_kind(pdata,k_poll); val_check(timeout,number); p = val_poll(pdata); tot = p->rcount + p->wcount; POSIX_LABEL(poll_events_again); if( poll(p->fds,tot,(int)(val_number(timeout) * 1000)) < 0 ) { HANDLE_EINTR(poll_events_again); neko_error(); } k = 0; for(i=0;ircount;i++) if( p->fds[i].revents & (POLLIN|POLLHUP) ) val_array_ptr(p->ridx)[k++] = alloc_int(i); val_array_ptr(p->ridx)[k] = alloc_int(-1); k = 0; for(;ifds[i].revents & (POLLOUT|POLLHUP) ) val_array_ptr(p->widx)[k++] = alloc_int(i - p->rcount); val_array_ptr(p->widx)[k] = alloc_int(-1); #endif return val_null; } /** socket_poll : 'socket array -> 'poll -> timeout:float -> 'socket array Perform a polling for data available over a given set of sockets. This is similar to [socket_select] except that [socket_select] is limited to a given number of simultaneous sockets to check. **/ static value socket_poll( value socks, value pdata, value timeout ) { polldata *p; value a; int i, rcount = 0; if( socket_poll_prepare(pdata,socks,alloc_array(0)) == NULL ) neko_error(); if( socket_poll_events(pdata,timeout) == NULL ) neko_error(); p = val_poll(pdata); while( val_array_ptr(p->ridx)[rcount] != alloc_int(-1) ) rcount++; a = alloc_array(rcount); for(i=0;iridx)[i])]; return a; } /** socket_set_fast_send : 'socket -> bool -> void Disable or enable to TCP_NODELAY flag for the socket **/ static value socket_set_fast_send( value s, value f ) { int fast; val_check_kind(s,k_socket); val_check(f,bool); fast = val_bool(f); if( setsockopt(val_sock(s),IPPROTO_TCP,TCP_NODELAY,(char*)&fast,sizeof(fast)) ) neko_error(); return val_null; } /** socket_set_broadcast : 'socket -> bool -> void Disable or enable broadcast option flag "SO_BROADCAST" for the socket **/ static value socket_set_broadcast( value s, value f ) { int broadcast; val_check_kind(s,k_socket); val_check(f,bool); broadcast = val_bool(f); if( setsockopt(val_sock(s),SOL_SOCKET,SO_BROADCAST,(char*)&broadcast,sizeof(broadcast)) ) neko_error(); return val_null; } /** socket_send_to : 'socket -> buf:string -> pos:int -> length:int -> addr:{host:'int32,port:int} -> int Send data from an unconnected UDP socket to the given address. **/ static value socket_send_to( value o, value data, value pos, value len, value vaddr ) { int p,l,dlen; value host, port; struct sockaddr_in addr; val_check_kind(o,k_socket); val_check(data,string); val_check(pos,int); val_check(len,int); val_check(vaddr,object); host = val_field(vaddr, f_host); port = val_field(vaddr, f_port); val_check(host,int32); val_check(port,int); p = val_int(pos); l = val_int(len); dlen = val_strlen(data); memset(&addr,0,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(val_int(port)); *(int*)&addr.sin_addr.s_addr = val_int32(host); if( p < 0 || l < 0 || p > dlen || p + l > dlen ) neko_error(); POSIX_LABEL(send_again); dlen = sendto(val_sock(o), val_string(data) + p , l, MSG_NOSIGNAL, (struct sockaddr*)&addr, sizeof(addr)); if( dlen == SOCKET_ERROR ) { HANDLE_EINTR(send_again); return block_error(); } return alloc_int(dlen); } /** socket_recv_from : 'socket -> buf:string -> pos:int -> length:int -> addr:{host:'int32,port:int} -> int Read data from an unconnected UDP socket, store the address from which we received data in addr. **/ static value socket_recv_from( value o, value data, value pos, value len, value addr ) { int p,l,dlen,ret; int retry = 0; struct sockaddr_in saddr; int slen = sizeof(saddr); val_check_kind(o,k_socket); val_check(data,string); val_check(pos,int); val_check(len,int); val_check(addr,object); p = val_int(pos); l = val_int(len); dlen = val_strlen(data); if( p < 0 || l < 0 || p > dlen || p + l > dlen ) neko_error(); POSIX_LABEL(recv_from_again); if( retry++ > NRETRYS ) { sock_tmp t; t.sock = val_sock(o); t.buf = val_string(data) + p; t.size = l; neko_thread_blocking(tmp_recv,&t); ret = t.ret; } else ret = recvfrom(val_sock(o), val_string(data) + p , l, MSG_NOSIGNAL, (struct sockaddr*)&saddr, &slen); if( ret == SOCKET_ERROR ) { HANDLE_EINTR(recv_from_again); #ifdef NEKO_WINDOWS if( WSAGetLastError() == WSAECONNRESET ) ret = 0; else #endif return block_error(); } alloc_field(addr,f_host,alloc_int32(*(int*)&saddr.sin_addr)); alloc_field(addr,f_port,alloc_int(ntohs(saddr.sin_port))); return alloc_int(ret); } /** socket_set_keepalive : 'socket -> bool -> time:int? -> interval:int? -> void Enable or disable TCP_KEEPALIVE flag for the socket **/ static value socket_set_keepalive( value o, value b, value time, value interval ) { int val; SOCKET s; val_check_kind(o,k_socket); val_check(b,bool); if( !val_is_null(time) || !val_is_null(interval) ){ val_check(time,int); val_check(interval,int); } s = val_sock(o); if( !val_bool(b) ) { val = 0; if( setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&val, sizeof(val)) != 0 ) neko_error(); } else { val = 1; if( setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&val, sizeof(val)) != 0 ) neko_error(); if( !val_is_null(time) && !val_is_null(interval) ) { # if defined(NEKO_WINDOWS) u_long params[3] = { 1, (unsigned long)val_int(time)*1000, (unsigned long)val_int(interval)*1000 }; if( WSAIoctl(s, SIO_KEEPALIVE_VALS, ¶ms, sizeof(params), NULL, 0, &val, NULL, NULL) != 0 ) neko_error(); # else # if defined(TCP_KEEPIDLE) val = val_int(time); if( setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&val, sizeof(val)) != 0 ) neko_error(); # elif defined(TCP_KEEPALIVE) val = val_int(time); if( setsockopt(s, IPPROTO_TCP, TCP_KEEPALIVE, (void *)&val, sizeof(val)) != 0 ) neko_error(); # endif # if defined(TCP_KEEPINTVL) val = val_int(interval); if( setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&val, sizeof(val)) != 0 ) neko_error(); # endif # endif } } return val_null; } /** socket_epoll_alloc : void -> 'epoll Allocate memory for edge/level-triggered polling (epoll). On Linux, this will use epoll; on other systems, this will fall back to select. **/ static value socket_epoll_alloc(value maxevents) { epolldata *ep; val_check(maxevents,int); ep = (epolldata*)alloc(sizeof(epolldata)); ep->maxevents = val_int(maxevents); ep->result = alloc_array(val_int(maxevents)); #ifndef HAS_EPOLL ep->read = alloc_array(FD_SETSIZE); ep->write = alloc_array(FD_SETSIZE); ep->rcount = 0; ep->wcount = 0; #else ep->epollfd = epoll_create1(0); ep->events = (struct epoll_event*)alloc(sizeof(struct epoll_event) * val_int(maxevents)); #endif return alloc_abstract(k_epoll, ep); } /** socket_epoll_register : 'epoll -> 'socket -> int Register a socket with an epoll instance to be notified of events. Returns the socket's fd. **/ static value socket_epoll_register(value e, value s, value events) { SOCKET sock; int event_types; epolldata *ep; val_check_kind(e,k_epoll); val_check_kind(s,k_socket); val_check(events,int); sock = val_sock(s); event_types = val_int(events); ep = val_epoll(e); #ifndef HAS_EPOLL if (sock >= FD_SETSIZE) val_throw(alloc_string("Can't register file descriptor >= FD_SETSIZE")); if (event_types & EPOLLIN) { if (ep->rcount >= FD_SETSIZE) val_throw(alloc_string("Too many sockets (on non-Linux platforms, 'epoll' uses select)")); val_array_ptr(ep->read)[ep->rcount++] = s; } if (event_types & EPOLLOUT) { if (ep->wcount >= FD_SETSIZE) val_throw(alloc_string("Too many sockets (on non-Linux platforms, 'epoll' uses select)")); val_array_ptr(ep->write)[ep->wcount++] = s; } #else struct epoll_event ev; ev.events = event_types; ev.data.fd = sock; int ret = epoll_ctl(ep->epollfd, EPOLL_CTL_ADD, sock, &ev); if (ret == -1) val_throw(alloc_int(errno)); #endif return alloc_int(sock); } /** socket_epoll_unregister : 'epoll -> 'socket -> int Unegister a socket with an epoll instance. Returns the socket's fd. **/ static value socket_epoll_unregister(value e, value s) { SOCKET sock; epolldata *ep; # ifndef HAS_EPOLL int i, j; # endif val_check_kind(e,k_epoll); val_check_kind(s,k_socket); sock = val_sock(s); ep = val_epoll(e); #ifndef HAS_EPOLL for (i = 0; i < ep->rcount; i++) { if (val_array_ptr(ep->read)[i] == s) { for (j = i+1; j < ep->rcount; j++) { val_array_ptr(ep->read)[j] = val_array_ptr(ep->read)[j-1]; } val_array_ptr(ep->read)[--ep->rcount] = NULL; --i; } } for (i = 0; i < ep->wcount; i++) { if (val_array_ptr(ep->write)[i] == s) { for (j = i+1; j < ep->wcount; j++) { val_array_ptr(ep->write)[j] = val_array_ptr(ep->write)[j-1]; } val_array_ptr(ep->write)[--ep->wcount] = NULL; --i; } } #else struct epoll_event ev; int ret = epoll_ctl(ep->epollfd, EPOLL_CTL_DEL, sock, &ev); if (ret == -1) return alloc_int(ret); else #endif return alloc_int(sock); } /** socket_epoll_wait : 'epoll -> int -> float -> int array Wait and return a list of socket fds with events. **/ static value socket_epoll_wait(value e, value timeout) { epolldata *ep; #ifndef HAS_EPOLL struct timeval t; SOCKET n = 0; bool indefinite; fd_set rx, wx; fd_set *ra, *wa; int i; int pos = 0; val_check_kind(e,k_epoll); ep = val_epoll(e); POSIX_LABEL(select_again); ra = ep->rcount == 0 ? NULL : make_socket_array(ep->read, ep->rcount, &rx, &n); wa = ep->wcount == 0 ? NULL : make_socket_array(ep->write, ep->wcount, &wx, &n); indefinite = val_is_null(timeout); if (!indefinite) { val_check(timeout,number); init_timeval(val_number(timeout),&t); } if( select((int)(n+1),ra,wa,NULL,indefinite ? NULL : &t) == SOCKET_ERROR ) { HANDLE_EINTR(select_again); val_throw(alloc_int(errno)); } if (ra != NULL) { for (i=0; i < ep->rcount && pos < ep->maxevents; i++) { value s = val_array_ptr(ep->read)[i]; if (FD_ISSET(val_sock(s),ra)) val_array_ptr(ep->result)[pos++] = alloc_int(val_sock(s)); } } if (wa != NULL) { for (i=0; i < ep->wcount && pos < ep->maxevents; i++) { value s = val_array_ptr(ep->write)[i]; if (FD_ISSET(val_sock(s),wa)) val_array_ptr(ep->result)[pos++] = alloc_int(val_sock(s)); } } val_set_size(ep->result,pos); return ep->result; #else int t; val_check_kind(e,k_epoll); ep = val_epoll(e); if (val_is_null(timeout)) t = -1; else { val_check(timeout,number); t = (int)(val_number(timeout)) * 1000; } int ret = epoll_wait(ep->epollfd, ep->events, ep->maxevents, t); if (ret == -1) val_throw(alloc_int(errno)); val_set_size(ep->result, ret); int i; for (i = 0; i < ret; i++) { val_array_ptr(ep->result)[i] = alloc_int(ep->events[i].data.fd); } return ep->result; #endif } DEFINE_PRIM(socket_init,0); DEFINE_PRIM(socket_new,1); DEFINE_PRIM(socket_send,4); DEFINE_PRIM(socket_send_char,2); DEFINE_PRIM(socket_recv,4); DEFINE_PRIM(socket_recv_char,1); DEFINE_PRIM(socket_write,2); DEFINE_PRIM(socket_read,1); DEFINE_PRIM(socket_close,1); DEFINE_PRIM(socket_connect,3); DEFINE_PRIM(socket_listen,2); DEFINE_PRIM(socket_select,4); DEFINE_PRIM(socket_bind,3); DEFINE_PRIM(socket_accept,1); DEFINE_PRIM(socket_peer,1); DEFINE_PRIM(socket_host,1); DEFINE_PRIM(socket_set_timeout,2); DEFINE_PRIM(socket_shutdown,3); DEFINE_PRIM(socket_set_blocking,2); DEFINE_PRIM(socket_set_fast_send,2); DEFINE_PRIM(socket_set_broadcast,2); DEFINE_PRIM(socket_send_to,5); DEFINE_PRIM(socket_recv_from,5); DEFINE_PRIM(socket_set_keepalive,4); DEFINE_PRIM(socket_poll_alloc,1); DEFINE_PRIM(socket_poll,3); DEFINE_PRIM(socket_poll_prepare,3); DEFINE_PRIM(socket_poll_events,2); DEFINE_PRIM(socket_epoll_alloc,1); DEFINE_PRIM(socket_epoll_register,3); DEFINE_PRIM(socket_epoll_unregister,2); DEFINE_PRIM(socket_epoll_wait,2); DEFINE_PRIM(host_local,0); DEFINE_PRIM(host_resolve,1); DEFINE_PRIM(host_to_string,1); DEFINE_PRIM(host_reverse,1); /* ************************************************************************ */ neko-2-4-0/libs/std/string.c000066400000000000000000000234751464615675700157040ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include /**

String Functions

Some useful functions dealing with string manipulation.

**/ /** string_split : s:string -> sep:string -> string list split the string [s] using separator [sep] **/ static value string_split( value o, value s ) { value l, first; int ilen; int slen; int start = 0; int pos; val_check(s,string); val_check(o,string); ilen = val_strlen(o); slen = val_strlen(s); l = NULL; first = NULL; for(pos=slen?0:1;pos<=ilen-slen;pos++) if( memcmp(val_string(o)+pos,val_string(s),slen) == 0 ) { value ss = copy_string(val_string(o)+start,pos-start); value l2 = alloc_array(2); val_array_ptr(l2)[0] = ss; val_array_ptr(l2)[1] = val_null; if( first == NULL ) first = l2; else val_array_ptr(l)[1] = l2; l = l2; start = pos + slen; if( slen ) pos = start - 1; } if( ilen > 0 && slen ) { value ss = start ? copy_string(val_string(o)+start,ilen-start) : o; value l2 = alloc_array(2); val_array_ptr(l2)[0] = ss; val_array_ptr(l2)[1] = val_null; if( first == NULL ) first = l2; else val_array_ptr(l)[1] = l2; } return (first == NULL)?val_null:first; } #define HEX 1 #define HEX_SMALL 2 /** sprintf : fmt:string -> params:(any | array) -> string Format a string. If only one parameter is needed then it can be directly passed, either the parameters need to be stored in an array. The following formats are accepted (with corresponding types) :
  • [%s] : string
  • [%d] [%x] [%X] : int
  • [%c] : int in the 0..255 range
  • [%b] : bool
  • [%f] : float
**/ static value neko_sprintf( value fmt, value params ) { char *last, *cur, *end; int count = 0; buffer b; val_check(fmt,string); b = alloc_buffer(NULL); last = val_string(fmt); cur = last; end = cur + val_strlen(fmt); while( cur != end ) { if( *cur == '%' ) { int width = 0, prec = 0, flags = 0; buffer_append_sub(b,last,cur - last); cur++; while( *cur >= '0' && *cur <= '9' ) { width = width * 10 + (*cur - '0'); cur++; } if( *cur == '.' ) { cur++; while( *cur >= '0' && *cur <= '9' ) { prec = prec * 10 + (*cur - '0'); cur++; } } if( *cur == '%' ) { buffer_append_sub(b,"%",1); cur++; } else { value param; if( count == 0 && !val_is_array(params) ) { // first ? param = params; count++; } else if( !val_is_array(params) || val_array_size(params) <= count ) neko_error(); else param = val_array_ptr(params)[count++]; switch( *cur ) { case 'c': { int c; char cc; val_check(param,int); c = val_int(param); if( c < 0 || c > 255 ) neko_error(); cc = (char)c; buffer_append_sub(b,&cc,1); } break; case 'x': flags |= HEX_SMALL; case 'X': flags |= HEX; case 'd': { char tmp[10]; int sign = 0; int size = 0; int tsize; int n; val_check(param,int); n = val_int(param); if( !(flags & HEX) && n < 0 ) { sign++; prec--; n = -n; } else if( n == 0 ) tmp[9-size++] = '0'; if( flags & HEX ) { unsigned int nn = (unsigned int)n; while( nn > 0 ) { int k = nn&15; if( k < 10 ) tmp[9-size++] = k + '0'; else tmp[9-size++] = (k - 10) + ((flags & HEX_SMALL)?'a':'A'); nn = nn >> 4; } } else { while( n > 0 ) { tmp[9-size++] = (n % 10) + '0'; n = n / 10; } } tsize = (size > prec)?size:prec + sign; while( width > tsize ) { width--; buffer_append_sub(b," ",1); } if( sign ) buffer_append_sub(b,"-",1); while( prec > size ) { prec--; buffer_append_sub(b,"0",1); } buffer_append_sub(b,tmp+10-size,size); } break; case 'f': { val_check(param,float); val_buffer(b,param); } break; case 's': { int size; int tsize; val_check(param,string); size = val_strlen(param); tsize = (size > prec)?size:prec; while( width > tsize ) { width--; buffer_append_sub(b," ",1); } while( prec > size ) { prec--; buffer_append_sub(b," ",1); } buffer_append_sub(b,val_string(param),size); } break; case 'b': { val_check(param,bool); buffer_append_sub(b,val_bool(param)?"true":"false",val_bool(param)?4:5); } break; default: neko_error(); break; } } cur++; last = cur; } else cur++; } buffer_append_sub(b,last,cur - last); return buffer_to_string(b); } /** url_decode : string -> string Decode an url using escaped format **/ static value url_decode( value v ) { val_check(v,string); { int pin = 0; int pout = 0; const char *in = val_string(v); int len = val_strlen(v); value v2 = alloc_empty_string(len); char *out = (char*)val_string(v2); while( len-- > 0 ) { char c = in[pin++]; if( c == '+' ) c = ' '; else if( c == '%' ) { int p1, p2; if( len < 2 ) break; p1 = in[pin++]; p2 = in[pin++]; len -= 2; if( p1 >= '0' && p1 <= '9' ) p1 -= '0'; else if( p1 >= 'a' && p1 <= 'f' ) p1 -= 'a' - 10; else if( p1 >= 'A' && p1 <= 'F' ) p1 -= 'A' - 10; else continue; if( p2 >= '0' && p2 <= '9' ) p2 -= '0'; else if( p2 >= 'a' && p2 <= 'f' ) p2 -= 'a' - 10; else if( p2 >= 'A' && p2 <= 'F' ) p2 -= 'A' - 10; else continue; c = (char)((unsigned char)((p1 << 4) + p2)); } out[pout++] = c; } out[pout] = 0; val_set_size(v2,pout); return v2; } } /** url_encode : string -> string Encode an url using escaped format **/ static value url_encode( value v ) { val_check(v,string); { int pin = 0; int pout = 0; const unsigned char *in = (const unsigned char*)val_string(v); static const char *hex = "0123456789ABCDEF"; int len = val_strlen(v); value v2 = alloc_empty_string(len * 3); unsigned char *out = (unsigned char*)val_string(v2); while( len-- > 0 ) { unsigned char c = in[pin++]; if( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c == '.' ) out[pout++] = c; else { out[pout++] = '%'; out[pout++] = hex[c >> 4]; out[pout++] = hex[c & 0xF]; } } out[pout] = 0; val_set_size(v2,pout); return v2; } } /** base_encode : s:string -> base:string -> string Encode a string using the specified base. The base length must be a power of two. **/ static value base_encode( value s, value base ) { int nbits; int len; int size; int mask; unsigned int buf; int curbits; value out; unsigned char *cin, *cout, *chars; val_check(s,string); val_check(base,string); len = val_strlen(base); cin = (unsigned char *)val_string(s); chars = (unsigned char *)val_string(base); nbits = 1; while( len > 1 << nbits ) nbits++; if( nbits > 8 || len != 1 << nbits ) neko_error(); size = (val_strlen(s) * 8 + nbits - 1) / nbits; out = alloc_empty_string(size); cout = (unsigned char *)val_string(out); buf = 0; curbits = 0; mask = ((1 << nbits) - 1); while( size-- > 0 ) { while( curbits < nbits ) { curbits += 8; buf <<= 8; buf |= *cin++; } curbits -= nbits; *cout++ = chars[(buf >> curbits) & mask]; } return out; } /** base_decode : s:string -> base:string -> string Decode a string encode in the specified base. The base length must be a power of two. **/ static value base_decode( value s, value base ) { int nbits; int len; int size; unsigned int buf; int curbits; value out; int i; int tbl[256]; unsigned char *cin, *cout, *chars; val_check(s,string); val_check(base,string); len = val_strlen(base); cin = (unsigned char *)val_string(s); chars = (unsigned char *)val_string(base); nbits = 1; while( len > 1 << nbits ) nbits++; if( nbits > 8 || len != 1 << nbits ) neko_error(); for(i=0;i<256;i++) tbl[i] = -1; for(i=0;i 0 ) { while( curbits < 8 ) { curbits += nbits; buf <<= nbits; i = tbl[*cin++]; if( i == -1 ) neko_error(); buf |= i; } curbits -= 8; *cout++ = (buf >> curbits) & 0xFF; } return out; } #define neko_sprintf__2 sprintf__2 DEFINE_PRIM(neko_sprintf,2); DEFINE_PRIM(string_split,2); DEFINE_PRIM(url_decode,1); DEFINE_PRIM(url_encode,1); DEFINE_PRIM(base_encode,2); DEFINE_PRIM(base_decode,2); /* ************************************************************************ */ neko-2-4-0/libs/std/sys.c000066400000000000000000000422121464615675700152020ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #ifdef NEKO_WINDOWS # include # include # include #else # include # include # include # include # include # include # include # include #ifdef NEKO_XLOCALE_H # include #else # include #endif #endif #ifdef NEKO_MAC # include # include # include #endif #ifndef CLK_TCK # define CLK_TCK 100 #endif /**

System

Interactions with the operating system.

**/ /** get_env : string -> string? Get some environment variable if exists **/ static value get_env( value v ) { char *s; val_check(v,string); s = getenv(val_string(v)); if( s == NULL ) return val_null; return alloc_string(s); } /** put_env : var:string -> val:string -> void Set some environment variable value **/ static value put_env( value e, value v ) { val_check(e,string); bool is_null = val_is_null(v); #ifdef NEKO_WINDOWS buffer b; if( !is_null ) val_check(v,string); b = alloc_buffer(NULL); val_buffer(b,e); buffer_append_sub(b,"=",1); if( !is_null ) val_buffer(b,v); if( putenv(val_string(buffer_to_string(b))) != 0 ) neko_error(); #else int result; if( is_null ) result = unsetenv(val_string(e)); else { val_check(v,string); result = setenv(val_string(e),val_string(v),1); } if( result != 0 ) neko_error(); #endif return val_true; } /** sys_sleep : number -> void Sleep a given number of seconds **/ static value sys_sleep( value f ) { val_check(f,number); #ifdef NEKO_WINDOWS Sleep((DWORD)(val_number(f) * 1000)); #else { struct timespec t; struct timespec tmp; t.tv_sec = (int)val_number(f); t.tv_nsec = (int)((val_number(f) - t.tv_sec) * 1e9); while( nanosleep(&t,&tmp) == -1 ) { if( errno != EINTR ) neko_error(); t = tmp; } } #endif return val_true; } /** set_time_locale : string -> bool Set the locale for LC_TIME, returns true on success **/ static value set_time_locale( value l ) { #ifdef NEKO_POSIX locale_t lc, old; val_check(l,string); lc = newlocale(LC_TIME_MASK,val_string(l),NULL); if( lc == NULL ) return val_false; old = uselocale(lc); if( old == NULL ) { freelocale(lc); return val_false; } if( old != LC_GLOBAL_LOCALE ) freelocale(old); return val_true; #else val_check(l,string); return alloc_bool(setlocale(LC_TIME,val_string(l)) != NULL); #endif } /** get_cwd : void -> string Return current working directory **/ static value get_cwd() { char buf[256]; int l; if( getcwd(buf,256) == NULL ) neko_error(); l = (int)strlen(buf); if( buf[l-1] != '/' && buf[l-1] != '\\' ) { buf[l] = '/'; buf[l+1] = 0; } return alloc_string(buf); } /** set_cwd : string -> void Set current working directory **/ static value set_cwd( value d ) { val_check(d,string); if( chdir(val_string(d)) ) neko_error(); return val_true; } /** sys_string : void -> string Return the local system string. The current value are possible :
  • [Windows]
  • [Linux]
  • [BSD]
  • [Mac]
**/ static value sys_string() { #if defined(NEKO_WINDOWS) || defined(NEKO_CYGWIN) return alloc_string("Windows"); #elif defined(NEKO_GNUKBSD) return alloc_string("GNU/kFreeBSD"); #elif defined(NEKO_LINUX) return alloc_string("Linux"); #elif defined(NEKO_BSD) return alloc_string("BSD"); #elif defined(NEKO_HURD) return alloc_string("GNU/Hurd"); #elif defined(NEKO_MAC) return alloc_string("Mac"); #else #error Unknow system string #endif } /** sys_is64 : void -> bool Returns true if we are on a 64-bit system **/ static value sys_is64() { #ifdef NEKO_64BITS return val_true; #else return val_false; #endif } /** sys_cpu_arch : void -> string Returns the cpu architecture. Current possible values:
  • [x86_64]
  • [x86]
  • [arm64]
  • [arm]
  • [riscv64]
  • [unknown]
**/ static value sys_cpu_arch() { #if defined(__x86_64__) || defined(_M_AMD64) return alloc_string("x86_64"); #elif defined(__i386__) || defined(_M_IX86) return alloc_string("x86"); #elif defined(__aarch64__) || defined(_M_ARM64) return alloc_string("arm64"); #elif defined(__arm__) || defined(_M_ARM) return alloc_string("arm"); #elif (defined(__riscv) && (__riscv_xlen == 64)) || defined(_M_RISCV64) return alloc_string("riscv64"); #else #warning Unknown CPU architecture for sys_cpu_arch return alloc_string("unknown"); #endif } /** sys_command : string -> int Run the shell command and return exit code **/ static value sys_command( value cmd ) { val_check(cmd,string); if( val_strlen(cmd) == 0 ) return alloc_int(-1); #ifdef NEKO_WINDOWS return alloc_int( system(val_string(cmd)) ); #else int status = system(val_string(cmd)); return alloc_int( WEXITSTATUS(status) | (WTERMSIG(status) << 8) ); #endif } /** sys_exit : int -> void Exit with the given errorcode. Never returns. **/ static value sys_exit( value ecode ) { val_check(ecode,int); exit(val_int(ecode)); return val_true; } /** sys_exists : string -> bool Returns true if the file or directory exists. **/ static value sys_exists( value path ) { struct stat st; val_check(path,string); return alloc_bool(stat(val_string(path),&st) == 0); } /** file_exists : string -> bool Deprecated : use sys_exists instead. **/ static value file_exists( value path ) { return sys_exists(path); } /** file_delete : string -> void Delete the file. Exception on error. **/ static value file_delete( value path ) { val_check(path,string); if( unlink(val_string(path)) != 0 ) neko_error(); return val_true; } /** sys_rename : from:string -> to:string -> void Rename the file or directory. Exception on error. **/ static value sys_rename( value path, value newname ) { val_check(path,string); val_check(newname,string); if( rename(val_string(path),val_string(newname)) != 0 ) neko_error(); return val_true; } #define STATF(f) alloc_field(o,val_id(#f),alloc_int(s.st_##f)) #define STATF32(f) alloc_field(o,val_id(#f),alloc_int32((int)s.st_##f)) /** sys_stat : string -> { gid => int, uid => int, atime => 'int32, mtime => 'int32, ctime => 'int32, dev => int, ino => int, nlink => int, rdev => int, mode => int, size => int } Run the [stat] command on the given file or directory. **/ static value sys_stat( value path ) { struct stat s; value o; val_check(path,string); if( stat(val_string(path),&s) != 0 ) neko_error(); o = alloc_object(NULL); STATF(gid); STATF(uid); STATF32(atime); STATF32(mtime); STATF32(ctime); STATF(dev); STATF(ino); STATF(mode); STATF(nlink); STATF(rdev); STATF(size); STATF(mode); return o; } /** sys_file_type : string -> string Return the type of the file. The current values are possible :
  • [file]
  • [dir]
  • [symlink]
  • [sock]
  • [char]
  • [block]
  • [fifo]
**/ static value sys_file_type( value path ) { struct stat s; val_check(path,string); if( stat(val_string(path),&s) != 0 ) neko_error(); if( s.st_mode & S_IFREG ) return alloc_string("file"); if( s.st_mode & S_IFDIR ) return alloc_string("dir"); if( s.st_mode & S_IFCHR ) return alloc_string("char"); #ifndef NEKO_WINDOWS if( s.st_mode & S_IFLNK ) return alloc_string("symlink"); if( s.st_mode & S_IFBLK ) return alloc_string("block"); if( s.st_mode & S_IFIFO ) return alloc_string("fifo"); if( s.st_mode & S_IFSOCK ) return alloc_string("sock"); #endif neko_error(); } /** sys_create_dir : string -> mode:int -> void Create a directory with the specified rights **/ static value sys_create_dir( value path, value mode ) { val_check(path,string); val_check(mode,int); #ifdef NEKO_WINDOWS if( mkdir(val_string(path)) != 0 ) #else if( mkdir(val_string(path),val_int(mode)) != 0 ) #endif neko_error(); return val_true; } /** sys_remove_dir : string -> void Remove a directory. Exception on error **/ static value sys_remove_dir( value path ) { val_check(path,string); if( rmdir(val_string(path)) != 0 ) neko_error(); return val_true; } /** sys_time : void -> float Return an accurate local time stamp in seconds since Jan 1 1970 **/ static value sys_time() { #ifdef NEKO_WINDOWS #define EPOCH_DIFF (134774*24*60*60.0) SYSTEMTIME t; FILETIME ft; ULARGE_INTEGER ui; GetSystemTime(&t); if( !SystemTimeToFileTime(&t,&ft) ) neko_error(); ui.LowPart = ft.dwLowDateTime; ui.HighPart = ft.dwHighDateTime; return alloc_float( ((tfloat)ui.QuadPart) / 10000000.0 - EPOCH_DIFF ); #else struct timeval tv; if( gettimeofday(&tv,NULL) != 0 ) neko_error(); return alloc_float( tv.tv_sec + ((tfloat)tv.tv_usec) / 1000000.0 ); #endif } /** sys_cpu_time : void -> float Return the most accurate CPU time spent since the process started (in seconds) **/ static value sys_cpu_time() { #ifdef NEKO_WINDOWS FILETIME unused; FILETIME stime; FILETIME utime; if( !GetProcessTimes(GetCurrentProcess(),&unused,&unused,&stime,&utime) ) neko_error(); return alloc_float( ((tfloat)(utime.dwHighDateTime+stime.dwHighDateTime)) * 65.536 * 6.5536 + (((tfloat)utime.dwLowDateTime + (tfloat)stime.dwLowDateTime) / 10000000) ); #else struct tms t; times(&t); return alloc_float( ((tfloat)(t.tms_utime + t.tms_stime)) / CLK_TCK ); #endif } /** sys_thread_cpu_time : void -> float Return the most accurate CPU time spent in user mode in the current thread (in seconds) **/ static value sys_thread_cpu_time() { #if defined(NEKO_WINDOWS) FILETIME unused; FILETIME utime; if( !GetThreadTimes(GetCurrentThread(),&unused,&unused,&unused,&utime) ) neko_error(); return alloc_float( ((tfloat)utime.dwHighDateTime) * 65.536 * 6.5536 + (((tfloat)utime.dwLowDateTime) / 10000000) ); #elif defined(NEKO_MAC) val_throw(alloc_string("sys_thread_cpu_time not implmented on OSX")); return val_null; #else struct timespec t; if( clock_gettime(CLOCK_THREAD_CPUTIME_ID,&t) ) neko_error(); return alloc_float( t.tv_sec + t.tv_nsec * 1e-9 ); #endif } /** sys_read_dir : string -> string list Return the content of a directory **/ static value sys_read_dir( value path ) { value h = val_null; value cur = NULL, tmp; #ifdef NEKO_WINDOWS WIN32_FIND_DATA d; HANDLE handle; buffer b; int len; val_check(path,string); len = val_strlen(path); b = alloc_buffer(NULL); val_buffer(b,path); if( len && val_string(path)[len-1] != '/' && val_string(path)[len-1] != '\\' ) buffer_append(b,"/*.*"); else buffer_append(b,"*.*"); path = buffer_to_string(b); handle = FindFirstFile(val_string(path),&d); if( handle == INVALID_HANDLE_VALUE ) neko_error(); while( true ) { // skip magic dirs if( d.cFileName[0] != '.' || (d.cFileName[1] != 0 && (d.cFileName[1] != '.' || d.cFileName[2] != 0)) ) { tmp = alloc_array(2); val_array_ptr(tmp)[0] = alloc_string(d.cFileName); val_array_ptr(tmp)[1] = val_null; if( cur ) val_array_ptr(cur)[1] = tmp; else h = tmp; cur = tmp; } if( !FindNextFile(handle,&d) ) break; } FindClose(handle); #else DIR *d; struct dirent *e; val_check(path,string); d = opendir(val_string(path)); if( d == NULL ) neko_error(); while( true ) { e = readdir(d); if( e == NULL ) break; // skip magic dirs if( e->d_name[0] == '.' && (e->d_name[1] == 0 || (e->d_name[1] == '.' && e->d_name[2] == 0)) ) continue; tmp = alloc_array(2); val_array_ptr(tmp)[0] = alloc_string(e->d_name); val_array_ptr(tmp)[1] = val_null; if( cur ) val_array_ptr(cur)[1] = tmp; else h = tmp; cur = tmp; } closedir(d); #endif return h; } /** file_full_path : string -> string Return an absolute path from a relative one. The file or directory must exists **/ static value file_full_path( value path ) { #ifdef NEKO_WINDOWS char buf[MAX_PATH+1]; val_check(path,string); if( GetFullPathName(val_string(path),MAX_PATH+1,buf,NULL) == 0 ) neko_error(); return alloc_string(buf); #elif defined(__GLIBC__) val_check(path,string); char *buf = realpath(val_string(path), NULL); if( buf == NULL ) neko_error(); value ret = alloc_string(buf); free(buf); return ret; #else char buf[PATH_MAX]; val_check(path,string); if( realpath(val_string(path),buf) == NULL ) neko_error(); return alloc_string(buf); #endif } /** sys_exe_path : void -> string Return the path of the executable **/ static value sys_exe_path() { #if defined(NEKO_WINDOWS) char path[MAX_PATH]; if( GetModuleFileName(NULL,path,MAX_PATH) == 0 ) neko_error(); return alloc_string(path); #elif defined(NEKO_MAC) char path[PATH_MAX+1]; uint32_t path_len = PATH_MAX; if( _NSGetExecutablePath(path, &path_len) ) neko_error(); return alloc_string(path); #elif defined(NEKO_LINUX) static char path[PATH_MAX]; int length = readlink("/proc/self/exe", path, sizeof(path)); if( length < 0 || length >= PATH_MAX ) { char *p = getenv(" "); // for upx if( p == NULL ) p = getenv("_"); if( p == NULL ) neko_error(); return alloc_string(p); } path[length] = '\0'; return alloc_string(path); #else const char *p = getenv("_"); if( p != NULL ) return alloc_string(p); neko_error(); #endif } #ifdef NEKO_MAC # define environ (*_NSGetEnviron()) #endif #ifndef NEKO_WINDOWS extern char **environ; #endif /** sys_env : void -> #list Return all the (key,value) pairs in the environment as a chained list **/ static value sys_env() { value h = val_null; value cur = NULL, tmp, key; char **e = environ; while( *e ) { char *x = strchr(*e,'='); if( x == NULL ) { e++; continue; } tmp = alloc_array(3); key = alloc_empty_string((int)(x - *e)); memcpy(val_string(key),*e,(int)(x - *e)); val_array_ptr(tmp)[0] = key; val_array_ptr(tmp)[1] = alloc_string(x+1); val_array_ptr(tmp)[2] = val_null; if( cur ) val_array_ptr(cur)[2] = tmp; else h = tmp; cur = tmp; e++; } return h; } /** sys_getch : bool -> int Read a character from stdin with or without echo **/ static value sys_getch( value b ) { # ifdef NEKO_WINDOWS val_check(b,bool); return alloc_int( val_bool(b)?getche():getch() ); # else // took some time to figure out how to do that // without relying on ncurses, which clear the // terminal on initscr() int c; struct termios term, old; val_check(b,bool); tcgetattr(fileno(stdin), &old); term = old; cfmakeraw(&term); tcsetattr(fileno(stdin), 0, &term); c = getchar(); tcsetattr(fileno(stdin), 0, &old); if( val_bool(b) ) fputc(c,stdout); return alloc_int(c); # endif } /** sys_get_pid : void -> int Returns the current process identifier **/ static value sys_get_pid() { # ifdef NEKO_WINDOWS return alloc_int(GetCurrentProcessId()); # else return alloc_int(getpid()); # endif } /** win_env_changed : void -> void Tell that the windows envionment variables were changed in the registry **/ static value win_env_changed() { # ifdef NEKO_WINDOWS DWORD unused; SendMessageTimeout(HWND_BROADCAST,WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_ABORTIFHUNG, 5000, &unused ); # endif return val_null; } DEFINE_PRIM(get_env,1); DEFINE_PRIM(put_env,2); DEFINE_PRIM(set_time_locale,1); DEFINE_PRIM(get_cwd,0); DEFINE_PRIM(set_cwd,1); DEFINE_PRIM(sys_sleep,1); DEFINE_PRIM(sys_command,1); DEFINE_PRIM(sys_exit,1); DEFINE_PRIM(sys_string,0); DEFINE_PRIM(sys_is64,0); DEFINE_PRIM(sys_cpu_arch,0); DEFINE_PRIM(sys_stat,1); DEFINE_PRIM(sys_time,0); DEFINE_PRIM(sys_cpu_time,0); DEFINE_PRIM(sys_env,0); DEFINE_PRIM(sys_create_dir,2); DEFINE_PRIM(sys_remove_dir,1); DEFINE_PRIM(sys_read_dir,1); DEFINE_PRIM(file_full_path,1); DEFINE_PRIM(file_exists,1); DEFINE_PRIM(sys_exists,1); DEFINE_PRIM(file_delete,1); DEFINE_PRIM(sys_rename,2); DEFINE_PRIM(sys_exe_path,0); DEFINE_PRIM(sys_file_type,1); DEFINE_PRIM(sys_getch,1); DEFINE_PRIM(sys_get_pid,0); DEFINE_PRIM(sys_thread_cpu_time,0); DEFINE_PRIM(win_env_changed,0); /* ************************************************************************ */ neko-2-4-0/libs/std/thread.c000066400000000000000000000350351464615675700156400ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #undef free_lock #undef lock_release #include #include #include #ifdef NEKO_WINDOWS # include typedef HANDLE vlock; #else # include # include typedef struct _vlock { pthread_mutex_t lock; pthread_cond_t cond; int counter; } *vlock; #endif typedef struct _tqueue { value msg; struct _tqueue *next; } tqueue; typedef struct { tqueue *first; tqueue *last; # ifdef NEKO_WINDOWS CRITICAL_SECTION lock; HANDLE wait; # else pthread_mutex_t lock; pthread_cond_t wait; # endif } vdeque; typedef struct { # ifdef NEKO_WINDOWS DWORD tid; # else pthread_t phandle; # endif value v; vdeque q; neko_vm *vm; } vthread; DECLARE_KIND(k_thread); #define val_thread(t) ((vthread*)val_data(t)) #ifdef NEKO_WINDOWS # define LOCK(l) EnterCriticalSection(&(l)) # define UNLOCK(l) LeaveCriticalSection(&(l)) # define SIGNAL(l) ReleaseSemaphore(l,1,NULL) #else # define LOCK(l) pthread_mutex_lock(&(l)) # define UNLOCK(l) pthread_mutex_unlock(&(l)) # define SIGNAL(l) pthread_cond_signal(&(l)) #endif /* deque raw API */ static void _deque_init( vdeque *q ) { # ifdef NEKO_WINDOWS q->wait = CreateSemaphore(NULL,0,(1 << 30),NULL); InitializeCriticalSection(&q->lock); # else pthread_mutex_init(&q->lock,NULL); pthread_cond_init(&q->wait,NULL); # endif } static void _deque_destroy( vdeque *q ) { #ifdef NEKO_WINDOWS DeleteCriticalSection(&q->lock); CloseHandle(q->wait); #else pthread_mutex_destroy(&q->lock); pthread_cond_destroy(&q->wait); #endif } static void _deque_add( vdeque *q, value msg ) { tqueue *t; t = (tqueue*)alloc(sizeof(tqueue)); t->msg = msg; t->next = NULL; LOCK(q->lock); if( q->last == NULL ) q->first = t; else q->last->next = t; q->last = t; SIGNAL(q->wait); UNLOCK(q->lock); } static void _deque_push( vdeque *q, value msg ) { tqueue *t; t = (tqueue*)alloc(sizeof(tqueue)); t->msg = msg; LOCK(q->lock); t->next = q->first; q->first = t; if( q->last == NULL ) q->last = t; SIGNAL(q->wait); UNLOCK(q->lock); } static value _deque_pop( vdeque *q, int block ) { value msg; LOCK(q->lock); while( q->first == NULL ) if( block ) { # ifdef NEKO_WINDOWS UNLOCK(q->lock); WaitForSingleObject(q->wait,INFINITE); LOCK(q->lock); # else pthread_cond_wait(&q->wait,&q->lock); # endif } else { UNLOCK(q->lock); return val_null; } msg = q->first->msg; q->first = q->first->next; if( q->first == NULL ) q->last = NULL; else SIGNAL(q->wait); UNLOCK(q->lock); return msg; } /**

Thread

An API to create and manager system threads and locks.

**/ #define val_lock(l) ((vlock)val_data(l)) #define val_tls(l) ((vtls*)val_data(l)) #define val_mutex(l) ((mt_lock*)val_data(l)) #define val_deque(l) ((vdeque*)val_data(l)) typedef struct { # ifdef NEKO_WINDOWS DWORD tls; # else pthread_key_t key; # endif } vtls; DEFINE_KIND(k_thread); DEFINE_KIND(k_lock); DEFINE_KIND(k_tls); DEFINE_KIND(k_mutex); DEFINE_KIND(k_deque); typedef struct { value callb; value callparam; vthread *t; void *handle; int jit; } tparams; static vthread *neko_thread_current() { return (vthread*)neko_vm_custom(neko_vm_current(),k_thread); } static void free_thread( value v ) { vthread *t = val_thread(v); _deque_destroy(&t->q); } static vthread *alloc_thread( neko_vm *vm ) { vthread *t = (vthread*)alloc(sizeof(vthread)); memset(t,0,sizeof(vthread)); #ifdef NEKO_WINDOWS t->tid = GetCurrentThreadId(); #else t->phandle = pthread_self(); #endif t->v = alloc_abstract(k_thread,t); t->vm = vm; _deque_init(&t->q); val_gc(t->v,free_thread); return t; } static void thread_init( void *_p ) { tparams *p = (tparams*)_p; neko_vm *vm; // init the VM and set current thread vm = neko_vm_alloc(NULL); p->t = alloc_thread(vm); neko_vm_jit(vm,p->jit); neko_vm_select(vm); neko_vm_set_custom(vm,k_thread,p->t); } static void thread_loop( void *_p ) { tparams *p = (tparams*)_p; value exc = NULL; val_callEx(val_null,p->callb,&p->callparam,1,&exc); // display exception if( exc != NULL ) { buffer b = alloc_buffer(NULL); fprintf(stderr,"An exception occurred in a neko Thread :\n"); val_buffer(b,exc); fprintf(stderr,"%s\n",val_string(buffer_to_string(b))); } // cleanup neko_vm_select(NULL); p->t->v = val_null; p->t->vm = NULL; } /** thread_create : f:function:1 -> p:any -> 'thread Creates a thread that will be running the function [f(p)] **/ static value thread_create( value f, value param ) { tparams *p; val_check_function(f,1); p = (tparams*)alloc(sizeof(tparams)); p->callb = f; p->callparam = param; p->jit = neko_vm_jit(neko_vm_current(),-1); if( !neko_thread_create(thread_init,thread_loop,p,&p->handle) ) neko_error(); return p->t->v; } /** thread_current : void -> 'thread Returns the current thread **/ static value thread_current() { vthread *t = neko_thread_current(); // should only occur for main thread ! if( t == NULL ) { neko_vm *vm = neko_vm_current(); t = alloc_thread(vm); neko_vm_set_custom(vm,k_thread,t); } return t->v; } /** thread_send : 'thread -> msg:any -> void Send a message into the target thread message queue **/ static value thread_send( value vt, value msg ) { vthread *t; val_check_kind(vt,k_thread); t = val_thread(vt); _deque_add(&t->q,msg); return val_null; } /** thread_read_message : block:bool -> any Reads a message from the message queue. If [block] is true, the function only returns when a message is available. If [block] is false and no message is available in the queue, the function will return immediatly [null]. **/ static value thread_read_message( value block ) { value v = thread_current(); vthread *t; if( v == NULL ) neko_error(); t = val_thread(v); val_check(block,bool); return _deque_pop( &t->q, val_bool(block) ); } /** thread_stack : 'thread -> array Get the thread current call stack. Might crash if the thread currently manipulate the stack, so mostly used to debug infinite loops. **/ static value thread_stack( value vt ) { vthread *t; val_check_kind(vt,k_thread); t = val_thread(vt); if( t->vm == NULL ) neko_error(); return neko_call_stack(t->vm); } static void free_lock( value l ) { # ifdef NEKO_WINDOWS CloseHandle( val_lock(l) ); # else pthread_cond_destroy( &val_lock(l)->cond ); pthread_mutex_destroy( &val_lock(l)->lock ); # endif } /** lock_create : void -> 'lock Creates a lock which is initially locked **/ static value lock_create() { value vl; vlock l; # ifdef NEKO_WINDOWS l = CreateSemaphore(NULL,0,(1 << 30),NULL); if( l == NULL ) neko_error(); # else l = (vlock)alloc_private(sizeof(struct _vlock)); l->counter = 0; if( pthread_mutex_init(&l->lock,NULL) != 0 || pthread_cond_init(&l->cond,NULL) != 0 ) neko_error(); # endif vl = alloc_abstract(k_lock,l); val_gc(vl,free_lock); return vl; } /** lock_release : 'lock -> void Release a lock. The thread does not need to own the lock to be able to release it. If a lock is released several times, it can be acquired as many times **/ static value lock_release( value lock ) { vlock l; val_check_kind(lock,k_lock); l = val_lock(lock); # ifdef NEKO_WINDOWS if( !ReleaseSemaphore(l,1,NULL) ) neko_error(); # else pthread_mutex_lock(&l->lock); l->counter++; pthread_cond_signal(&l->cond); pthread_mutex_unlock(&l->lock); # endif return val_true; } /** lock_wait : 'lock -> timeout:number? -> bool Waits for a lock to be released and acquire it. If [timeout] (in seconds) is not null and expires then the returned value is false **/ static value lock_wait( value lock, value timeout ) { int has_timeout = !val_is_null(timeout); val_check_kind(lock,k_lock); if( has_timeout ) val_check(timeout,number); # ifdef NEKO_WINDOWS switch( WaitForSingleObject(val_lock(lock),has_timeout?(DWORD)(val_number(timeout) * 1000.0):INFINITE) ) { case WAIT_ABANDONED: case WAIT_OBJECT_0: return val_true; case WAIT_TIMEOUT: return val_false; default: neko_error(); } # else { vlock l = val_lock(lock); pthread_mutex_lock(&l->lock); while( l->counter == 0 ) { if( has_timeout ) { struct timeval tv; struct timespec t; double delta = val_number(timeout); int idelta = (int)delta, idelta2; delta -= idelta; delta *= 1.0e9; gettimeofday(&tv,NULL); delta += tv.tv_usec * 1000.0; idelta2 = (int)(delta / 1e9); delta -= idelta2 * 1e9; t.tv_sec = tv.tv_sec + idelta + idelta2; t.tv_nsec = (long)delta; if( pthread_cond_timedwait(&l->cond,&l->lock,&t) != 0 ) { pthread_mutex_unlock(&l->lock); return val_false; } } else pthread_cond_wait(&l->cond,&l->lock); } l->counter--; if( l->counter > 0 ) pthread_cond_signal(&l->cond); pthread_mutex_unlock(&l->lock); return val_true; } # endif } static void free_tls( value v ) { vtls *t = val_tls(v); # ifdef NEKO_WINDOWS TlsFree(t->tls); # else pthread_key_delete(t->key); # endif free(t); } /** tls_create : void -> 'tls Creates thread local storage. This is placeholder that can store a value that will be different depending on the local thread. You must set the tls value to [null] before exiting the thread or the memory will never be collected. **/ static value tls_create() { value v; vtls *t = (vtls*)malloc(sizeof(vtls)); # ifdef NEKO_WINDOWS t->tls = TlsAlloc(); TlsSetValue(t->tls,NULL); # else pthread_key_create(&t->key,NULL); # endif v = alloc_abstract(k_tls,t); val_gc(v,free_tls); return v; } /** tls_get : 'tls -> any Returns the value set by [tls_set] for the local thread. **/ static value tls_get( value v ) { vtls *t; value *r; val_check_kind(v,k_tls); t = val_tls(v); # ifdef NEKO_WINDOWS r = (value*)TlsGetValue(t->tls); # else r = (value*)pthread_getspecific(t->key); # endif if( r == NULL ) return val_null; return *r; } /** tls_set : 'tls -> any -> void Set the value of the TLS for the local thread. **/ static value tls_set( value v, value content ) { vtls *t; value *r; val_check_kind(v,k_tls); t = val_tls(v); # ifdef NEKO_WINDOWS r = (value*)TlsGetValue(t->tls); # else r = (value*)pthread_getspecific(t->key); # endif if( r == NULL ) { if( val_is_null(content) ) return val_null; r = alloc_root(1); # ifdef NEKO_WINDOWS TlsSetValue(t->tls,r); # else pthread_setspecific(t->key,r); # endif } else if( val_is_null(content) ) { free_root(r); # ifdef NEKO_WINDOWS TlsSetValue(t->tls,NULL); # else pthread_setspecific(t->key,NULL); # endif return val_null; } *r = content; return val_null; } static void free_mutex( value v ) { neko_free_lock( val_mutex(v) ); } /** mutex_create : void -> 'mutex Creates a mutex, which can be used to acquire a temporary lock to access some ressource. The main difference with a lock is that a mutex must always be released by the owner thread. **/ static value mutex_create() { mt_lock *m = neko_alloc_lock(); value v = alloc_abstract(k_mutex,m); val_gc(v,free_mutex); return v; } /** mutex_acquire : 'mutex -> void The current thread acquire the mutex or wait if not available. The same thread can acquire several times the same mutex but must release it as many times it has been acquired. **/ static value mutex_acquire( value m ) { val_check_kind(m,k_mutex); neko_lock_acquire( val_mutex(m) ); return val_null; } /** mutex_try : 'mutex -> bool Try to acquire the mutex, returns true if acquire or false if it's already locked by another thread. **/ static value mutex_try( value m ) { val_check_kind(m,k_mutex); return alloc_bool( neko_lock_try(val_mutex(m)) ); } /** mutex_release : 'mutex -> void Release a mutex that has been acquired by the current thread. The behavior is undefined if the current thread does not own the mutex. **/ static value mutex_release( value m ) { val_check_kind(m,k_mutex); neko_lock_release(val_mutex(m)); return val_null; } static void free_deque( value v ) { _deque_destroy(val_deque(v)); } /** deque_create : void -> 'deque create a message queue for multithread access **/ static value deque_create() { vdeque *q = (vdeque*)alloc(sizeof(vdeque)); value v = alloc_abstract(k_deque,q); val_gc(v,free_deque); _deque_init(q); return v; } /** deque_add : 'deque -> any -> void add a message at the end of the queue **/ static value deque_add( value v, value i ) { val_check_kind(v,k_deque); _deque_add(val_deque(v),i); return val_null; } /** deque_push : 'deque -> any -> void add a message at the head of the queue **/ static value deque_push( value v, value i ) { val_check_kind(v,k_deque); _deque_push(val_deque(v),i); return val_null; } /** deque_pop : 'deque -> bool -> any? pop a message from the queue head. Either block until a message is available or return immedialtly with null. **/ static value deque_pop( value v, value block ) { val_check_kind(v,k_deque); val_check(block,bool); return _deque_pop(val_deque(v),val_bool(block)); } DEFINE_PRIM(thread_create,2); DEFINE_PRIM(thread_current,0); DEFINE_PRIM(thread_send,2); DEFINE_PRIM(thread_read_message,1); DEFINE_PRIM(thread_stack,1); DEFINE_PRIM(lock_create,0); DEFINE_PRIM(lock_wait,2); DEFINE_PRIM(lock_release,1); DEFINE_PRIM(tls_create,0); DEFINE_PRIM(tls_set,2); DEFINE_PRIM(tls_get,1); DEFINE_PRIM(mutex_create,0); DEFINE_PRIM(mutex_acquire,1); DEFINE_PRIM(mutex_try,1); DEFINE_PRIM(mutex_release,1); DEFINE_PRIM(deque_create,0); DEFINE_PRIM(deque_add,2); DEFINE_PRIM(deque_push,2); DEFINE_PRIM(deque_pop,2); neko-2-4-0/libs/std/unicase.c000066400000000000000000000671271464615675700160270ustar00rootroot00000000000000#define UL_BITS 6 #define UL_SIZE 64 static uchar _E[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L1[UL_SIZE] = {0,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L3[UL_SIZE] = {224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,0,248,249,250,251,252,253,254,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L4[UL_SIZE] = {257,0,259,0,261,0,263,0,265,0,267,0,269,0,271,0,273,0,275,0,277,0,279,0,281,0,283,0,285,0,287,0,289,0,291,0,293,0,295,0,297,0,299,0,301,0,303,0,105,0,307,0,309,0,311,0,0,314,0,316,0,318,0,320}; static uchar L5[UL_SIZE] = {0,322,0,324,0,326,0,328,0,0,331,0,333,0,335,0,337,0,339,0,341,0,343,0,345,0,347,0,349,0,351,0,353,0,355,0,357,0,359,0,361,0,363,0,365,0,367,0,369,0,371,0,373,0,375,0,255,378,0,380,0,382,0,0}; static uchar L6[UL_SIZE] = {0,595,387,0,389,0,596,392,0,598,599,396,0,0,477,601,603,402,0,608,611,0,617,616,409,0,0,0,623,626,0,629,417,0,419,0,421,0,640,424,0,643,0,0,429,0,648,432,0,650,651,436,0,438,0,658,441,0,0,0,445,0,0,0}; static uchar L7[UL_SIZE] = {0,0,0,0,454,454,0,457,457,0,460,460,0,462,0,464,0,466,0,468,0,470,0,472,0,474,0,476,0,0,479,0,481,0,483,0,485,0,487,0,489,0,491,0,493,0,495,0,0,499,499,0,501,0,405,447,505,0,507,0,509,0,511,0}; static uchar L8[UL_SIZE] = {513,0,515,0,517,0,519,0,521,0,523,0,525,0,527,0,529,0,531,0,533,0,535,0,537,0,539,0,541,0,543,0,414,0,547,0,549,0,551,0,553,0,555,0,557,0,559,0,561,0,563,0,0,0,0,0,0,0,11365,572,0,410,11366,0}; static uchar L9[UL_SIZE] = {0,578,0,384,649,652,583,0,585,0,587,0,589,0,591,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L13[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,881,0,883,0,0,0,887,0,0,0,0,0,0,0,0,1011}; static uchar L14[UL_SIZE] = {0,0,0,0,0,0,940,0,941,942,943,0,972,0,973,974,0,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,0,963,964,965,966,967,968,969,970,971,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L15[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,983,0,0,0,0,0,0,0,0,985,0,987,0,989,0,991,0,993,0,995,0,997,0,999,0,1001,0,1003,0,1005,0,1007,0,0,0,0,0,952,0,0,1016,0,1010,1019,0,0,891,892,893}; static uchar L16[UL_SIZE] = {1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L17[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1121,0,1123,0,1125,0,1127,0,1129,0,1131,0,1133,0,1135,0,1137,0,1139,0,1141,0,1143,0,1145,0,1147,0,1149,0,1151,0}; static uchar L18[UL_SIZE] = {1153,0,0,0,0,0,0,0,0,0,1163,0,1165,0,1167,0,1169,0,1171,0,1173,0,1175,0,1177,0,1179,0,1181,0,1183,0,1185,0,1187,0,1189,0,1191,0,1193,0,1195,0,1197,0,1199,0,1201,0,1203,0,1205,0,1207,0,1209,0,1211,0,1213,0,1215,0}; static uchar L19[UL_SIZE] = {1231,1218,0,1220,0,1222,0,1224,0,1226,0,1228,0,1230,0,0,1233,0,1235,0,1237,0,1239,0,1241,0,1243,0,1245,0,1247,0,1249,0,1251,0,1253,0,1255,0,1257,0,1259,0,1261,0,1263,0,1265,0,1267,0,1269,0,1271,0,1273,0,1275,0,1277,0,1279,0}; static uchar L20[UL_SIZE] = {1281,0,1283,0,1285,0,1287,0,1289,0,1291,0,1293,0,1295,0,1297,0,1299,0,1301,0,1303,0,1305,0,1307,0,1309,0,1311,0,1313,0,1315,0,1317,0,1319,0,1321,0,1323,0,1325,0,1327,0,0,1377,1378,1379,1380,1381,1382,1383,1384,1385,1386,1387,1388,1389,1390,1391}; static uchar L21[UL_SIZE] = {1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L66[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11520,11521,11522,11523,11524,11525,11526,11527,11528,11529,11530,11531,11532,11533,11534,11535,11536,11537,11538,11539,11540,11541,11542,11543,11544,11545,11546,11547,11548,11549,11550,11551}; static uchar L67[UL_SIZE] = {11552,11553,11554,11555,11556,11557,0,11559,0,0,0,0,0,11565,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L120[UL_SIZE] = {7681,0,7683,0,7685,0,7687,0,7689,0,7691,0,7693,0,7695,0,7697,0,7699,0,7701,0,7703,0,7705,0,7707,0,7709,0,7711,0,7713,0,7715,0,7717,0,7719,0,7721,0,7723,0,7725,0,7727,0,7729,0,7731,0,7733,0,7735,0,7737,0,7739,0,7741,0,7743,0}; static uchar L121[UL_SIZE] = {7745,0,7747,0,7749,0,7751,0,7753,0,7755,0,7757,0,7759,0,7761,0,7763,0,7765,0,7767,0,7769,0,7771,0,7773,0,7775,0,7777,0,7779,0,7781,0,7783,0,7785,0,7787,0,7789,0,7791,0,7793,0,7795,0,7797,0,7799,0,7801,0,7803,0,7805,0,7807,0}; static uchar L122[UL_SIZE] = {7809,0,7811,0,7813,0,7815,0,7817,0,7819,0,7821,0,7823,0,7825,0,7827,0,7829,0,0,0,0,0,0,0,0,0,223,0,7841,0,7843,0,7845,0,7847,0,7849,0,7851,0,7853,0,7855,0,7857,0,7859,0,7861,0,7863,0,7865,0,7867,0,7869,0,7871,0}; static uchar L123[UL_SIZE] = {7873,0,7875,0,7877,0,7879,0,7881,0,7883,0,7885,0,7887,0,7889,0,7891,0,7893,0,7895,0,7897,0,7899,0,7901,0,7903,0,7905,0,7907,0,7909,0,7911,0,7913,0,7915,0,7917,0,7919,0,7921,0,7923,0,7925,0,7927,0,7929,0,7931,0,7933,0,7935,0}; static uchar L124[UL_SIZE] = {0,0,0,0,0,0,0,0,7936,7937,7938,7939,7940,7941,7942,7943,0,0,0,0,0,0,0,0,7952,7953,7954,7955,7956,7957,0,0,0,0,0,0,0,0,0,0,7968,7969,7970,7971,7972,7973,7974,7975,0,0,0,0,0,0,0,0,7984,7985,7986,7987,7988,7989,7990,7991}; static uchar L125[UL_SIZE] = {0,0,0,0,0,0,0,0,8000,8001,8002,8003,8004,8005,0,0,0,0,0,0,0,0,0,0,0,8017,0,8019,0,8021,0,8023,0,0,0,0,0,0,0,0,8032,8033,8034,8035,8036,8037,8038,8039,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L126[UL_SIZE] = {0,0,0,0,0,0,0,0,8064,8065,8066,8067,8068,8069,8070,8071,0,0,0,0,0,0,0,0,8080,8081,8082,8083,8084,8085,8086,8087,0,0,0,0,0,0,0,0,8096,8097,8098,8099,8100,8101,8102,8103,0,0,0,0,0,0,0,0,8112,8113,8048,8049,8115,0,0,0}; static uchar L127[UL_SIZE] = {0,0,0,0,0,0,0,0,8050,8051,8052,8053,8131,0,0,0,0,0,0,0,0,0,0,0,8144,8145,8054,8055,0,0,0,0,0,0,0,0,0,0,0,0,8160,8161,8058,8059,8165,0,0,0,0,0,0,0,0,0,0,0,8056,8057,8060,8061,8179,0,0,0}; static uchar L132[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,969,0,0,0,107,229,0,0,0,0,0,0,8526,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L133[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8570,8571,8572,8573,8574,8575,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L134[UL_SIZE] = {0,0,0,8580,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L146[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9424,9425,9426,9427,9428,9429,9430,9431,9432,9433}; static uchar L147[UL_SIZE] = {9434,9435,9436,9437,9438,9439,9440,9441,9442,9443,9444,9445,9446,9447,9448,9449,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L176[UL_SIZE] = {11312,11313,11314,11315,11316,11317,11318,11319,11320,11321,11322,11323,11324,11325,11326,11327,11328,11329,11330,11331,11332,11333,11334,11335,11336,11337,11338,11339,11340,11341,11342,11343,11344,11345,11346,11347,11348,11349,11350,11351,11352,11353,11354,11355,11356,11357,11358,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L177[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11361,0,619,7549,637,0,0,11368,0,11370,0,11372,0,593,625,592,594,0,11379,0,0,11382,0,0,0,0,0,0,0,0,575,576}; static uchar L178[UL_SIZE] = {11393,0,11395,0,11397,0,11399,0,11401,0,11403,0,11405,0,11407,0,11409,0,11411,0,11413,0,11415,0,11417,0,11419,0,11421,0,11423,0,11425,0,11427,0,11429,0,11431,0,11433,0,11435,0,11437,0,11439,0,11441,0,11443,0,11445,0,11447,0,11449,0,11451,0,11453,0,11455,0}; static uchar L179[UL_SIZE] = {11457,0,11459,0,11461,0,11463,0,11465,0,11467,0,11469,0,11471,0,11473,0,11475,0,11477,0,11479,0,11481,0,11483,0,11485,0,11487,0,11489,0,11491,0,0,0,0,0,0,0,0,11500,0,11502,0,0,0,0,11507,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L665[UL_SIZE] = {42561,0,42563,0,42565,0,42567,0,42569,0,42571,0,42573,0,42575,0,42577,0,42579,0,42581,0,42583,0,42585,0,42587,0,42589,0,42591,0,42593,0,42595,0,42597,0,42599,0,42601,0,42603,0,42605,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L666[UL_SIZE] = {42625,0,42627,0,42629,0,42631,0,42633,0,42635,0,42637,0,42639,0,42641,0,42643,0,42645,0,42647,0,42649,0,42651,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L668[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42787,0,42789,0,42791,0,42793,0,42795,0,42797,0,42799,0,0,0,42803,0,42805,0,42807,0,42809,0,42811,0,42813,0,42815,0}; static uchar L669[UL_SIZE] = {42817,0,42819,0,42821,0,42823,0,42825,0,42827,0,42829,0,42831,0,42833,0,42835,0,42837,0,42839,0,42841,0,42843,0,42845,0,42847,0,42849,0,42851,0,42853,0,42855,0,42857,0,42859,0,42861,0,42863,0,0,0,0,0,0,0,0,0,0,42874,0,42876,0,7545,42879,0}; static uchar L670[UL_SIZE] = {42881,0,42883,0,42885,0,42887,0,0,0,0,42892,0,613,0,0,42897,0,42899,0,0,0,42903,0,42905,0,42907,0,42909,0,42911,0,42913,0,42915,0,42917,0,42919,0,42921,0,614,604,609,620,0,0,670,647,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L1020[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,0,0,0,0,0}; static uchar L1040[UL_SIZE] = {66600,66601,66602,66603,66604,66605,66606,66607,66608,66609,66610,66611,66612,66613,66614,66615,66616,66617,66618,66619,66620,66621,66622,66623,66624,66625,66626,66627,66628,66629,66630,66631,66632,66633,66634,66635,66636,66637,66638,66639,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar L1122[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71872,71873,71874,71875,71876,71877,71878,71879,71880,71881,71882,71883,71884,71885,71886,71887,71888,71889,71890,71891,71892,71893,71894,71895,71896,71897,71898,71899,71900,71901,71902,71903}; static uchar U1[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,0,0,0,0,0}; static uchar U2[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,924,0,0,0,0,0,0,0,0,0,0}; static uchar U3[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,0,216,217,218,219,220,221,222,376}; static uchar U4[UL_SIZE] = {0,256,0,258,0,260,0,262,0,264,0,266,0,268,0,270,0,272,0,274,0,276,0,278,0,280,0,282,0,284,0,286,0,288,0,290,0,292,0,294,0,296,0,298,0,300,0,302,0,73,0,306,0,308,0,310,0,0,313,0,315,0,317,0}; static uchar U5[UL_SIZE] = {319,0,321,0,323,0,325,0,327,0,0,330,0,332,0,334,0,336,0,338,0,340,0,342,0,344,0,346,0,348,0,350,0,352,0,354,0,356,0,358,0,360,0,362,0,364,0,366,0,368,0,370,0,372,0,374,0,0,377,0,379,0,381,83}; static uchar U6[UL_SIZE] = {579,0,0,386,0,388,0,0,391,0,0,0,395,0,0,0,0,0,401,0,0,502,0,0,0,408,573,0,0,0,544,0,0,416,0,418,0,420,0,0,423,0,0,0,0,428,0,0,431,0,0,0,435,0,437,0,0,440,0,0,0,444,0,503}; static uchar U7[UL_SIZE] = {0,0,0,0,453,453,453,456,456,456,459,459,459,0,461,0,463,0,465,0,467,0,469,0,471,0,473,0,475,398,0,478,0,480,0,482,0,484,0,486,0,488,0,490,0,492,0,494,0,498,498,498,0,500,0,0,0,504,0,506,0,508,0,510}; static uchar U8[UL_SIZE] = {0,512,0,514,0,516,0,518,0,520,0,522,0,524,0,526,0,528,0,530,0,532,0,534,0,536,0,538,0,540,0,542,0,0,0,546,0,548,0,550,0,552,0,554,0,556,0,558,0,560,0,562,0,0,0,0,0,0,0,0,571,0,0,11390}; static uchar U9[UL_SIZE] = {11391,0,577,0,0,0,0,582,0,584,0,586,0,588,0,590,11375,11373,11376,385,390,0,393,394,0,399,0,400,42923,0,0,0,403,42924,0,404,0,42893,42922,0,407,406,0,11362,42925,0,0,412,0,11374,413,0,0,415,0,0,0,0,0,0,0,11364,0,0}; static uchar U10[UL_SIZE] = {422,0,0,425,0,0,0,42929,430,580,433,434,581,0,0,0,0,0,439,0,0,0,0,0,0,0,0,0,0,0,42928,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U13[UL_SIZE] = {0,0,0,0,0,921,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,880,0,882,0,0,0,886,0,0,0,1021,1022,1023,0,0}; static uchar U14[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,902,904,905,906,0,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927}; static uchar U15[UL_SIZE] = {928,929,931,931,932,933,934,935,936,937,938,939,908,910,911,0,914,920,0,0,0,934,928,975,0,984,0,986,0,988,0,990,0,992,0,994,0,996,0,998,0,1000,0,1002,0,1004,0,1006,922,929,1017,895,0,917,0,0,1015,0,0,1018,0,0,0,0}; static uchar U16[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055}; static uchar U17[UL_SIZE] = {1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,0,1120,0,1122,0,1124,0,1126,0,1128,0,1130,0,1132,0,1134,0,1136,0,1138,0,1140,0,1142,0,1144,0,1146,0,1148,0,1150}; static uchar U18[UL_SIZE] = {0,1152,0,0,0,0,0,0,0,0,0,1162,0,1164,0,1166,0,1168,0,1170,0,1172,0,1174,0,1176,0,1178,0,1180,0,1182,0,1184,0,1186,0,1188,0,1190,0,1192,0,1194,0,1196,0,1198,0,1200,0,1202,0,1204,0,1206,0,1208,0,1210,0,1212,0,1214}; static uchar U19[UL_SIZE] = {0,0,1217,0,1219,0,1221,0,1223,0,1225,0,1227,0,1229,1216,0,1232,0,1234,0,1236,0,1238,0,1240,0,1242,0,1244,0,1246,0,1248,0,1250,0,1252,0,1254,0,1256,0,1258,0,1260,0,1262,0,1264,0,1266,0,1268,0,1270,0,1272,0,1274,0,1276,0,1278}; static uchar U20[UL_SIZE] = {0,1280,0,1282,0,1284,0,1286,0,1288,0,1290,0,1292,0,1294,0,1296,0,1298,0,1300,0,1302,0,1304,0,1306,0,1308,0,1310,0,1312,0,1314,0,1316,0,1318,0,1320,0,1322,0,1324,0,1326,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U21[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359}; static uchar U22[UL_SIZE] = {1360,1361,1362,1363,1364,1365,1366,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U117[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42877,0,0,0,11363,0,0}; static uchar U120[UL_SIZE] = {0,7680,0,7682,0,7684,0,7686,0,7688,0,7690,0,7692,0,7694,0,7696,0,7698,0,7700,0,7702,0,7704,0,7706,0,7708,0,7710,0,7712,0,7714,0,7716,0,7718,0,7720,0,7722,0,7724,0,7726,0,7728,0,7730,0,7732,0,7734,0,7736,0,7738,0,7740,0,7742}; static uchar U121[UL_SIZE] = {0,7744,0,7746,0,7748,0,7750,0,7752,0,7754,0,7756,0,7758,0,7760,0,7762,0,7764,0,7766,0,7768,0,7770,0,7772,0,7774,0,7776,0,7778,0,7780,0,7782,0,7784,0,7786,0,7788,0,7790,0,7792,0,7794,0,7796,0,7798,0,7800,0,7802,0,7804,0,7806}; static uchar U122[UL_SIZE] = {0,7808,0,7810,0,7812,0,7814,0,7816,0,7818,0,7820,0,7822,0,7824,0,7826,0,7828,0,0,0,0,0,7776,0,0,0,0,0,7840,0,7842,0,7844,0,7846,0,7848,0,7850,0,7852,0,7854,0,7856,0,7858,0,7860,0,7862,0,7864,0,7866,0,7868,0,7870}; static uchar U123[UL_SIZE] = {0,7872,0,7874,0,7876,0,7878,0,7880,0,7882,0,7884,0,7886,0,7888,0,7890,0,7892,0,7894,0,7896,0,7898,0,7900,0,7902,0,7904,0,7906,0,7908,0,7910,0,7912,0,7914,0,7916,0,7918,0,7920,0,7922,0,7924,0,7926,0,7928,0,7930,0,7932,0,7934}; static uchar U124[UL_SIZE] = {7944,7945,7946,7947,7948,7949,7950,7951,0,0,0,0,0,0,0,0,7960,7961,7962,7963,7964,7965,0,0,0,0,0,0,0,0,0,0,7976,7977,7978,7979,7980,7981,7982,7983,0,0,0,0,0,0,0,0,7992,7993,7994,7995,7996,7997,7998,7999,0,0,0,0,0,0,0,0}; static uchar U125[UL_SIZE] = {8008,8009,8010,8011,8012,8013,0,0,0,0,0,0,0,0,0,0,0,8025,0,8027,0,8029,0,8031,0,0,0,0,0,0,0,0,8040,8041,8042,8043,8044,8045,8046,8047,0,0,0,0,0,0,0,0,8122,8123,8136,8137,8138,8139,8154,8155,8184,8185,8170,8171,8186,8187,0,0}; static uchar U126[UL_SIZE] = {8072,8073,8074,8075,8076,8077,8078,8079,0,0,0,0,0,0,0,0,8088,8089,8090,8091,8092,8093,8094,8095,0,0,0,0,0,0,0,0,8104,8105,8106,8107,8108,8109,8110,8111,0,0,0,0,0,0,0,0,8120,8121,0,8124,0,0,0,0,0,0,0,0,0,0,921,0}; static uchar U127[UL_SIZE] = {0,0,0,8140,0,0,0,0,0,0,0,0,0,0,0,0,8152,8153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8168,8169,0,0,0,8172,0,0,0,0,0,0,0,0,0,0,0,0,0,8188,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U133[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,8498,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,8555,8556,8557,8558,8559}; static uchar U134[UL_SIZE] = {0,0,0,0,8579,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U147[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9398,9399,9400,9401,9402,9403,9404,9405,9406,9407,9408,9409,9410,9411,9412,9413,9414,9415,9416,9417,9418,9419,9420,9421,9422,9423,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U176[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11264,11265,11266,11267,11268,11269,11270,11271,11272,11273,11274,11275,11276,11277,11278,11279}; static uchar U177[UL_SIZE] = {11280,11281,11282,11283,11284,11285,11286,11287,11288,11289,11290,11291,11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303,11304,11305,11306,11307,11308,11309,11310,0,0,11360,0,0,0,570,574,0,11367,0,11369,0,11371,0,0,0,0,0,0,11378,0,0,11381,0,0,0,0,0,0,0,0,0}; static uchar U178[UL_SIZE] = {0,11392,0,11394,0,11396,0,11398,0,11400,0,11402,0,11404,0,11406,0,11408,0,11410,0,11412,0,11414,0,11416,0,11418,0,11420,0,11422,0,11424,0,11426,0,11428,0,11430,0,11432,0,11434,0,11436,0,11438,0,11440,0,11442,0,11444,0,11446,0,11448,0,11450,0,11452,0,11454}; static uchar U179[UL_SIZE] = {0,11456,0,11458,0,11460,0,11462,0,11464,0,11466,0,11468,0,11470,0,11472,0,11474,0,11476,0,11478,0,11480,0,11482,0,11484,0,11486,0,11488,0,11490,0,0,0,0,0,0,0,0,11499,0,11501,0,0,0,0,11506,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U180[UL_SIZE] = {4256,4257,4258,4259,4260,4261,4262,4263,4264,4265,4266,4267,4268,4269,4270,4271,4272,4273,4274,4275,4276,4277,4278,4279,4280,4281,4282,4283,4284,4285,4286,4287,4288,4289,4290,4291,4292,4293,0,4295,0,0,0,0,0,4301,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U665[UL_SIZE] = {0,42560,0,42562,0,42564,0,42566,0,42568,0,42570,0,42572,0,42574,0,42576,0,42578,0,42580,0,42582,0,42584,0,42586,0,42588,0,42590,0,42592,0,42594,0,42596,0,42598,0,42600,0,42602,0,42604,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U666[UL_SIZE] = {0,42624,0,42626,0,42628,0,42630,0,42632,0,42634,0,42636,0,42638,0,42640,0,42642,0,42644,0,42646,0,42648,0,42650,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U668[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42786,0,42788,0,42790,0,42792,0,42794,0,42796,0,42798,0,0,0,42802,0,42804,0,42806,0,42808,0,42810,0,42812,0,42814}; static uchar U669[UL_SIZE] = {0,42816,0,42818,0,42820,0,42822,0,42824,0,42826,0,42828,0,42830,0,42832,0,42834,0,42836,0,42838,0,42840,0,42842,0,42844,0,42846,0,42848,0,42850,0,42852,0,42854,0,42856,0,42858,0,42860,0,42862,0,0,0,0,0,0,0,0,0,0,42873,0,42875,0,0,42878}; static uchar U670[UL_SIZE] = {0,42880,0,42882,0,42884,0,42886,0,0,0,0,42891,0,0,0,0,42896,0,42898,0,0,0,42902,0,42904,0,42906,0,42908,0,42910,0,42912,0,42914,0,42916,0,42918,0,42920,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U1021[UL_SIZE] = {0,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U1040[UL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66560,66561,66562,66563,66564,66565,66566,66567,66568,66569,66570,66571,66572,66573,66574,66575,66576,66577,66578,66579,66580,66581,66582,66583}; static uchar U1041[UL_SIZE] = {66584,66585,66586,66587,66588,66589,66590,66591,66592,66593,66594,66595,66596,66597,66598,66599,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uchar U1123[UL_SIZE] = {71840,71841,71842,71843,71844,71845,71846,71847,71848,71849,71850,71851,71852,71853,71854,71855,71856,71857,71858,71859,71860,71861,71862,71863,71864,71865,71866,71867,71868,71869,71870,71871,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; #define LMAX 1123 #define UMAX 1124 static uchar *LOWER[LMAX] = {_E,L1,_E,L3,L4,L5,L6,L7,L8,L9,_E,_E,_E,L13,L14,L15,L16,L17,L18,L19,L20,L21,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,L66,L67,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,L120,L121,L122,L123,L124,L125,L126,L127,_E,_E,_E,_E,L132,L133,L134,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,L146,L147,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,L176,L177,L178,L179,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,L665,L666,_E,L668,L669,L670,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,L1020,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,L1040,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,L1122}; static uchar *UPPER[UMAX] = {_E,U1,U2,U3,U4,U5,U6,U7,U8,U9,U10,_E,_E,U13,U14,U15,U16,U17,U18,U19,U20,U21,U22,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,U117,_E,_E,U120,U121,U122,U123,U124,U125,U126,U127,_E,_E,_E,_E,_E,U133,U134,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,U147,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,U176,U177,U178,U179,U180,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,U665,U666,_E,U668,U669,U670,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,U1021,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,U1040,U1041,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,_E,U1123}; neko-2-4-0/libs/std/unicode.c000066400000000000000000000527201464615675700160170ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include /**

Unicode

Operations on Unicode strings. Most of the operations are optimized for speed so they might still succeed on some malformed Unicode string. The only function that completely check the Unicode format is [unicode_validate]. Other functions might raise some exception or not depending on the malformed data.

Supported encodings are "ascii", "iso-latin1", "utf8", "ucs2-le", "ucs2-be", "utf16-le", "utf16-be", "utf32-le", "utf32-be"

**/ #define INVALID_CHAR 0xFFFFFFFF typedef unsigned int uchar; typedef unsigned char *ustring; #include "unicase.c" #define val_ustring(s) ((ustring)val_string(s)) typedef enum { ASCII = 0, ISO_LATIN1 = 1, UTF8 = 2, UCS2_BE = 3, UCS2_LE = 4, UTF16_BE = 5, UTF16_LE = 6, UTF32_BE = 7, UTF32_LE = 8, LAST_ENCODING = 9 } encoding; static const char *encodings[] = { "ascii", "iso-latin1", "utf8", "ucs2-le", "ucs2-be", "utf16-le", "utf16-be", "utf32-le", "utf32-be", }; static int utf8_codelen[16] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 3, 4 }; DEFINE_KIND(k_uni_buf); #define IS_BE(e) (neko_is_big_endian() ^ ((e) & 1)) #define u16be(v) (((v) >> 8) | (((v) << 8) & 0xFF00)) #define u32be(v) (((v) >> 24) | (((v) >> 8) & 0xFF00) | (((v) << 8) & 0xFF0000) | ((v) << 24)) static void TODO() { val_throw(alloc_string("Not implemented")); } /* ------------ UCHAR LIB FUNCTIONS -------------------- */ static int uchar_size( uchar c, encoding e ) { switch( e ) { case ASCII: if( c >= 0x80 ) return 0; return 1; case ISO_LATIN1: if( c >= 0x100 ) return 0; return 1; case UCS2_LE: case UCS2_BE: if( c >= 0x10000 ) return 0; return 2; case UTF16_LE: case UTF16_BE: if( c >= 0x110000 ) return 0; return c >= 0x10000 ? 4 : 2; case UTF32_LE: case UTF32_BE: return 4; case UTF8: if( c >= 0x200000 ) return 0; return utf8_codelen[c>>4]; default: TODO(); break; } return 0; } static void uchar_set( ustring str, encoding e, uchar c ) { switch( e ) { case ASCII: case ISO_LATIN1: *str = c; break; case UTF8: if( c < 0x80 ) *str++ = c; else if( c < 0x800 ) { *str++ = (c >> 6); *str++ = (c & 63); } else if( c < 0x10000 ) { *str++ = 0xE0 | (c >> 12); *str++ = 0x80 | ((c >> 6) & 63); *str++ = 0x80 | (c & 63); } else { *str++ = 0xF0 | (c >> 18); *str++ = 0x80 | ((c >> 12) & 63); *str++ = 0x80 | ((c >> 6) & 63); *str++ = 0x80 | (c & 63); } break; case UCS2_LE: case UCS2_BE: if( IS_BE(e) ) c = u16be(c); *((unsigned short*)str) = c; break; case UTF32_LE: case UTF32_BE: if( IS_BE(e) ) c = u32be(c); *((unsigned int*)str) = c; break; } } static ustring uchar_pos( ustring str, int size, encoding e, int pos ) { if( pos < 0 ) return NULL; switch( e ) { case ASCII: case ISO_LATIN1: if( pos > size ) return NULL; return str + pos; case UCS2_LE: case UCS2_BE: if( pos > (size >> 1) ) return NULL; return str + (pos << 1); case UTF32_LE: case UTF32_BE: if( pos > (size >> 2) ) return NULL; return str + (pos << 2); case UTF16_LE: case UTF16_BE: { ustring end = str + size; int dec = IS_BE(e) ? 0 : 8; while( str + 1 < end && pos-- > 0 ) { unsigned short c = *(unsigned short *)str; if( ((c >> dec) & 0xFC) == 0xD8 ) str += 4; else str += 2; } if( str > end ) return NULL; return str; } case UTF8: { ustring end = str + size; while( str < end && pos-- > 0 ) { int l = utf8_codelen[(*str)>>4]; if( l == 0 ) return NULL; str += l; } if( str > end ) return NULL; return str; } default: TODO(); break; } return NULL; } static uchar uchar_get( ustring *rstr, int size, encoding e, int pos ) { uchar c; ustring str = *rstr; if( pos < 0 ) return INVALID_CHAR; switch( e ) { case ASCII: case ISO_LATIN1: if( pos >= size ) return INVALID_CHAR; str = str + pos; c = *str++; break; case UCS2_LE: case UCS2_BE: if( pos >= size << 1 ) return INVALID_CHAR; str = str + (pos<<1); c = *((unsigned short *)str); str += 2; if( IS_BE(e) ) c = u16be(c); break; case UTF32_LE: case UTF32_BE: if( pos >= size << 2 ) return INVALID_CHAR; str = str + (pos<<2); c = *((unsigned int *)str); str += 4; if( IS_BE(e) ) c = u32be(c); break; case UTF16_LE: case UTF16_BE: if( pos > 0 ) { ustring str2 = uchar_pos(str, size, e, pos); size = (str + size) - str2; str = str2; } if( size < 2 ) return INVALID_CHAR; c = *(unsigned short *)str; str += 2; if( IS_BE(e) ) c = u16be(c); if( (c & 0xFC00) == 0xD800 ) { unsigned short c2; if( size < 4 ) return INVALID_CHAR; c2 = *(unsigned short *)str; str += 2; if( IS_BE(e) ) c2 = u16be(c2); if( (c2 & 0xFC00) != 0xDC00 ) return INVALID_CHAR; c = (((c&0x3FFF)<<10) | (c2&0x3FFF)) + 0x10000; } break; case UTF8: if( pos > 0 ) { ustring str2 = uchar_pos(str, size, e, pos); size = (str + size) - str2; str = str2; } if( size < 1 ) return INVALID_CHAR; c = *str; if( c >= 0x80 ) { int len = utf8_codelen[c>>4]; if( len == 0 || size < len ) return INVALID_CHAR; if( c < 0xE0 ) { c = ((c & 0x3F) << 6) | ((*str) & 0x7F); str++; } else if( c < 0xF0 ) { c = ((c & 0x1F) << 12) | (((*str) & 0x7F) << 6) | (str[1] & 0x7F); str += 2; } else { c = ((c & 0x0F) << 18) | (((*str) & 0x7F) << 12) | ((str[1] & 0x7F) << 6) | (str[2] & 0x7F); str += 3; } } break; default: c = INVALID_CHAR; TODO(); break; } *rstr = str; return c; } /* -------------------------------------------------------------------- */ /** unicode_encoding : string -> int Gets the encoding code corresponding the given value ascii=0 iso-latin1=1 utf8=2 ucs2-be=3 ucs2-le=4 utf16-be=5 utf16-le=6 utf32-be=7 utf32-le=8 **/ static value unicode_encoding_code( value str ) { int i; val_check(str, string); for( i = 0; i < LAST_ENCODING; i++ ) if( strcmp(val_string(str), encodings[i]) == 0 ) return alloc_int(i); neko_error(); return val_null; } static value unicode_encoding_string( value enc ) { val_check(enc, int); if( val_int(enc) < 0 || val_int(enc) >= LAST_ENCODING ) neko_error(); return alloc_string(encodings[val_int(enc)]); } typedef struct { value buf; encoding enc; int pos; int nchars; } uni_buf; static encoding get_encoding( value v ) { int e; if( !val_is_int(v) || (e = val_int(v)) < 0 || e >= LAST_ENCODING ) val_throw(alloc_string("Invalid encoding value")); return e; } /** unicode_buf_alloc : size:int -> encoding:int -> 'ubuf Create a new buffer with an initial size in bytes and specific encoding. **/ static value unicode_buf_alloc( value size, value encoding ) { uni_buf *b; val_check(size,int); if( val_int(size) < 0 ) neko_error(); b = (uni_buf*)alloc(sizeof(uni_buf)); b->buf = alloc_empty_string(val_int(size)); b->pos = 0; b->nchars = 0; b->enc = get_encoding(encoding); return alloc_abstract(k_uni_buf,b); } static void unicode_buf_resize( uni_buf *b ) { value s; int len = val_strlen(b->buf); int size2 = (len * 3) >> 1; if( size2 - len < 10 ) size2 = 10 + len; s = alloc_empty_string(size2); memcpy(val_string(s),val_string(b->buf),len); b->buf = s; } /** unicode_buf_add : 'buf -> int -> void Add an Unicode char to the buffer **/ static value unicode_buf_add( value buf, value uc ) { uni_buf *b; uchar c; int req; val_check_kind(buf,k_uni_buf); val_check(uc,int); b = (uni_buf*)val_data(buf); c = (uchar)val_int(uc); req = uchar_size(c, b->enc); if( req == 0 ) val_throw(alloc_string("Unicode char outside of encoding range")); if( b->pos + req > val_strlen(b->buf) ) unicode_buf_resize(b); uchar_set(val_ustring(b->buf) + b->pos, b->enc, c); return val_null; } /** unicode_buf_content : 'buf -> string Return the current content of the buffer. This is not a copy of the buffer but the shared content. Retreiving content and then continuing to add chars is possible but not very efficient. **/ static value unicode_buf_content( value buf ) { uni_buf *b; val_check_kind(buf,k_uni_buf); b = (uni_buf*)val_data(buf); val_set_length(b->buf,b->pos); val_string(b->buf)[b->pos] = 0; return b->buf; } /** unicode_buf_length : 'buf -> int Return the number of Unicode chars stored in the buffer **/ static value unicode_buf_length( value buf ) { uni_buf *b; val_check_kind(buf,k_uni_buf); b = (uni_buf*)val_data(buf); return alloc_int(b->nchars); } /** unicode_buf_size : 'buf -> int Return the current size in bytes of the buffer **/ static value unicode_buf_size( value buf ) { uni_buf *b; val_check_kind(buf,k_uni_buf); b = (uni_buf*)val_data(buf); return alloc_int(b->pos); } /** unicode_validate : s:string -> encoding:int -> bool Tells if a string [s] is encoded using the given Unicode format. **/ static value unicode_validate( value str, value encoding ) { int l; ustring s; val_check(str,string); l = val_strlen(str); s = val_ustring(str); switch( get_encoding(encoding) ) { case ASCII: while( l-- ) { unsigned char c = *s++; if( c >= 0x80 ) return val_false; } break; case ISO_LATIN1: return val_true; case UCS2_LE: if( s[0] == 0xFE && s[1] == 0xFF ) return val_false; // BOM fail return alloc_bool((l & 1) == 0); case UCS2_BE: if( s[0] == 0xFF && s[1] == 0xFE ) return val_false; // BOM fail return alloc_bool((l & 1) == 0); case UTF32_LE: if( l >= 4 && s[0] == 0 && s[1] == 0 && s[2] == 0xFE && s[3] == 0xFF ) return val_false; // BOM fail return alloc_bool((l & 3) == 0); case UTF32_BE: if( l >= 4 && s[0] == 0xFF && s[1] == 0xFE && s[2] == 0 && s[3] == 0 ) return val_false; // BOM fail return alloc_bool((l & 3) == 0); case UTF16_LE: if( s[0] == 0xFE && s[1] == 0xFF ) return val_false; // BOM fail { ustring end = s + l; int dec = IS_BE(UTF16_LE) ? 0 : 8; while( s + 1 < end ) { unsigned short c = *(unsigned short*)s; if( ((c >> dec) & 0xFC) == 0xD8 ) { if( s + 3 >= end ) return val_false; if( ((((unsigned short*)s)[1] >> dec) & 0xFC) != 0xDC ) return val_false; s += 4; } else s += 2; } return alloc_bool(s == end); } case UTF16_BE: if( s[0] == 0xFF && s[1] == 0xFE ) return val_false; // BOM fail { ustring end = s + l; int dec = IS_BE(UTF16_BE) ? 8 : 0; while( s + 1 < end ) { unsigned short c = *(unsigned short*)s; if( ((c >> dec) & 0xFC) == 0xD8 ) { if( s + 3 >= end ) return val_false; if( ((((unsigned short*)s)[1] >> dec) & 0xFC) != 0xDC ) return val_false; s += 4; } else s += 2; } return alloc_bool(s == end); } case UTF8: { ustring end = s + l; while( s < end ) { int l = utf8_codelen[(*s) >> 4]; if( l == 0 ) return val_false; s += l; } return alloc_bool(s == end); } default: TODO(); break; } return val_false; } /** unicode_length : string -> encoding:int -> int Returns the number of Unicode chars in the string. **/ static value unicode_length( value str, value enc ) { int l; int count = 0; encoding e; ustring s; val_check(str,string); l = val_strlen(str); s = val_ustring(str); e = get_encoding(enc); switch( e ) { case ASCII: case ISO_LATIN1: count = l; break; case UCS2_LE: case UCS2_BE: count = l >> 1; break; case UTF16_LE: case UTF16_BE: { ustring end = s + l; int dec = IS_BE(e) ? 0 : 8; while( s + 1 < end ) { unsigned short c = *(unsigned short *)s; if( ((c >> dec) & 0xFC) == 0xD8 ) s += 4; else s += 2; count++; } if( s > end ) count--; } break; case UTF32_LE: case UTF32_BE: count = l >> 2; break; case UTF8: { ustring end = s + l; while( s < end ) { int l = utf8_codelen[(*s) >> 4]; if( l == 0 ) l = 1; count++; s += l; } if( s > end ) count--; } break; default: TODO(); break; } return alloc_int(count); } /** unicode_sub : string -> encoding:int -> pos:int -> len:int -> string Returns a part of an Unicode string. **/ static value unicode_sub( value str, value enc, value vpos, value vlen ) { int l; int pos, len; encoding e; ustring s, start, end; val_check(str,string); val_check(vpos,int); val_check(vlen,int); e = get_encoding(enc); l = val_strlen(str); pos = val_int(vpos); len = val_int(vlen); s = val_ustring(str); start = uchar_pos(s,l,e,pos); if( start == NULL ) neko_error(); end = uchar_pos(start,l-(start-s),e,len); if( end == NULL ) neko_error(); l = (int)(end - start); str = alloc_empty_string(l); memcpy(val_string(str),start,l); return str; } /** unicode_get : string -> encoding:int -> n:int -> int Returns the [n]th char in an Unicode string. This might be inefficient if [n] is big and the string has variable length per char. **/ static value unicode_get( value str, value enc, value pos ) { uchar c; ustring s; val_check(str,string); val_check(pos,int); s = val_ustring(str); c = uchar_get(&s, val_strlen(str), get_encoding(enc), val_int(pos)); if( c == INVALID_CHAR ) neko_error(); return alloc_best_int(c); } /** unicode_iter : string -> encoding:int -> f:(int -> void) -> void Call [f] with each of Unicode char of the string. **/ static value unicode_iter( value str, value enc, value f ) { encoding e; ustring s, end; val_check(str,string); val_check_function(f,1); e = get_encoding(enc); s = val_ustring(str); end = s + val_strlen(str); while( s < end ) { uchar c = uchar_get(&s, end - s, e, 0); if( c == INVALID_CHAR ) neko_error(); val_call1(f,alloc_best_int(c)); } return val_null; } /** unicode_compare : s1:string -> s2:string -> encoding:int -> int Compare two Unicode strings according to their char codes. **/ static value unicode_compare( value str1, value str2, value enc ) { int l1, l2, l; encoding e; ustring s1, s2; val_check(str1,string); val_check(str2,string); l1 = val_strlen(str1); l2 = val_strlen(str2); s1 = val_ustring(str1); s2 = val_ustring(str2); l = (l1 < l2)?l1:l2; e = get_encoding(enc); switch( e ) { case ISO_LATIN1: case ASCII: case UCS2_BE: case UTF32_BE: { int r = memcmp(s1,s2,l); if( r != 0 ) return alloc_int(r); break; } case UCS2_LE: { unsigned short *i1 = (unsigned short*)s1; unsigned short *i2 = (unsigned short*)s2; int i, d; for( i = 0; i < (l>>1); i++ ) { unsigned short c1 = i1[i]; unsigned short c2 = i2[i]; if( IS_BE(UCS2_LE) ) { c1 = u16be(c1); c2 = u16be(c2); } d = c1 - c2; if( d != 0 ) return alloc_int(d < 0 ? -1 : 1); } break; } case UTF32_LE: { unsigned int *i1 = (unsigned int*)s1; unsigned int *i2 = (unsigned int*)s2; int i, d; for( i = 0; i < (l>>2); i++ ) { unsigned int c1 = i1[i]; unsigned int c2 = i2[i]; if( IS_BE(UTF32_LE) ) { c1 = u32be(c1); c2 = u32be(c2); } d = c1 - c2; if( d != 0 ) return alloc_int(d < 0 ? -1 : 1); } break; } case UTF16_BE: case UTF16_LE: { unsigned short *i1 = (unsigned short*)s1; unsigned short *i2 = (unsigned short*)s2; unsigned short *end1 = i1 + (l1>>1); unsigned short *end2 = i2 + (l2>>1); int d; while( i1 < end1 && i2 < end2 ) { unsigned short c1 = *i1++; unsigned short c2 = *i2++; if( IS_BE(e) ) { c1 = u16be(c1); c2 = u16be(c2); } if( (c1 & 0xFC00) == 0xD800 ) { if( i1 == end1 ) neko_error(); c1 = (((c1&0x3FFF)<<10) | ((*i1++)&0x3FFF)) + 0x10000; } if( (c2 & 0xFC00) == 0xD800 ) { if( i2 == end2 ) neko_error(); c2 = (((c2&0x3FFF)<<10) | ((*i2++)&0x3FFF)) + 0x10000; } d = c1 - c2; if( d != 0 ) return alloc_int(d < 0 ? -1 : 1); } d = (end1 - i1) - (end2 - i2); return alloc_int( d == 0 ? 0 : d < 0 ? -1 : 1 ); } case UTF8: // assume that we have correctly encoded the code points // so they both take the minimum required number of stored bytes while( l-- ) { unsigned char c1 = *s1++; unsigned char c2 = *s2++; if( c1 != c2 ) return alloc_int((c1 > c2)?-1:1); if( c1 < 0x7F ) continue; else if( c1 < 0xC0 ) neko_error(); else if( c1 < 0xE0 ) { l--; if( l < 0 ) neko_error(); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?-1:1); } else if( c1 < 0xF0 ) { l-=2; if( l < 0 ) neko_error(); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?-1:1); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?-1:1); } else { l -= 3; if( l < 0 ) neko_error(); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?-1:1); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?-1:1); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?-1:1); } } default: TODO(); break; } if( l1 != l2 ) return alloc_int((l1 > l2)?1:-1); return alloc_int(0); } static void expand( value *str, int *len ) { int len2 = ((*len) * 5) >> 2; value v2; if( len2 - (*len) < 10 ) len2 = (*len) + 10; v2 = alloc_empty_string(len2); memcpy(val_string(v2), val_string(*str), *len); *len = len2; *str = v2; } /** unicode_convert : string -> encoding:int -> to_encoding:int -> string Convert an Unicode string from a given encoding to another. **/ static value unicode_convert( value str, value encoding, value to_encoding ) { ustring s, end; int e, e_to; int len, size, pos = 0; value vto; val_check(str,string); s = val_ustring(str); size = val_strlen(str); end = s + size; e = get_encoding(encoding); e_to = get_encoding(to_encoding); if( e == e_to ) return str; // try to allocate enough space at first guess switch( e ) { case ISO_LATIN1: case UTF8: case ASCII: len = size; break; case UCS2_LE: case UCS2_BE: case UTF16_LE: case UTF16_BE: len = size >> 1; break; case UTF32_LE: case UTF32_BE: len = size >> 2; break; default: TODO(); len = 0; break; } switch( e_to ) { case ISO_LATIN1: case ASCII: break; case UTF8: // assume one byte per char break; case UCS2_LE: case UCS2_BE: case UTF16_LE: case UTF16_BE: len *= 2; break; case UTF32_LE: case UTF32_BE: len *= 4; break; default: TODO(); break; } // convert vto = alloc_empty_string(len); while( s < end ) { uchar c = uchar_get(&s,end - s,e,0); int k; if( c == INVALID_CHAR ) val_throw(alloc_string("Input string is not correctly encoded")); k = uchar_size(c, e_to); if( k == 0 ) { if( e_to == ISO_LATIN1 || e_to == ASCII ) c = '?'; else c = 0xFFFD; k = uchar_size(c, e_to); } if( pos + k > len ) expand(&vto,&len); uchar_set(val_string(vto) + pos, e_to, c); pos += k; } val_set_length(vto, pos); val_string(vto)[pos] = 0; return vto; } /** unicode_lower : string -> encoding:int -> string Returns the lowercase version of the unicode string. **/ static value unicode_lower( value str, value enc ) { ustring s,end; value out; int pos = 0; int len; encoding e; val_check(str,string); e = get_encoding(enc); s = val_ustring(str); len = val_strlen(str); end = s + len; out = alloc_empty_string(len); while( s < end ) { uchar c = uchar_get(&s,end - s, e, 0); int up = c >> UL_BITS; int k; if( up < LMAX ) { uchar c2 = LOWER[up][c&((1< len ) expand(&out, &len); uchar_set(val_ustring(out)+pos, e, c); pos += k; } val_set_length(out, pos); val_string(out)[pos] = 0; return out; } /** unicode_upper : string -> encoding:int -> string Returns the lowercase version of the unicode string. **/ static value unicode_upper( value str, value enc ) { ustring s,end; value out; int pos = 0; int len; encoding e; val_check(str,string); e = get_encoding(enc); s = val_ustring(str); len = val_strlen(str); end = s + len; out = alloc_empty_string(len); while( s < end ) { uchar c = uchar_get(&s,end - s, e, 0); int up = c >> UL_BITS; int k; if( up < UMAX ) { uchar c2 = UPPER[up][c&((1< len ) expand(&out, &len); uchar_set(val_ustring(out)+pos, e, c); pos += k; } val_set_length(out, pos); val_string(out)[pos] = 0; return out; } DEFINE_PRIM(unicode_buf_alloc,2); DEFINE_PRIM(unicode_buf_add,2); DEFINE_PRIM(unicode_buf_content,1); DEFINE_PRIM(unicode_buf_length,1); DEFINE_PRIM(unicode_buf_size,1); DEFINE_PRIM(unicode_get,3); DEFINE_PRIM(unicode_validate,2); DEFINE_PRIM(unicode_iter,3); DEFINE_PRIM(unicode_length,2); DEFINE_PRIM(unicode_compare,3); DEFINE_PRIM(unicode_sub,4); DEFINE_PRIM(unicode_convert,3); DEFINE_PRIM(unicode_lower,2); DEFINE_PRIM(unicode_upper,2); /* ************************************************************************ */ neko-2-4-0/libs/std/utf8.c000066400000000000000000000244551464615675700152630ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include /**

UTF8

Operations on UTF8 strings. Most of the operations are optimized for speed so they might still succeed on some malformed UTF8 string. The only function that completely check the UTF8 format is [utf8_validate]. Other functions might raise some exception or not depending on the malformed data.

**/ typedef struct { value buf; int pos; int nesc; } ubuf; DEFINE_KIND(k_ubuf); /** utf8_buf_alloc : size:int -> 'ubuf Create a new buffer with an initial size in bytes **/ static value utf8_buf_alloc( value size ) { ubuf *b; val_check(size,int); if( val_int(size) < 0 ) neko_error(); b = (ubuf*)alloc(sizeof(ubuf)); b->buf = alloc_empty_string(val_int(size)); b->nesc = 0; b->pos = 0; return alloc_abstract(k_ubuf,b); } static void utf8_buf_resize( ubuf *b ) { value s; int len = val_strlen(b->buf); // allocate a number of bytes depending on previously // escaped caracters, with a minimum of 10 int nbytes = (b->nesc + b->pos * 2 - 1) / (b->pos?b->pos:1); if( nbytes < 10 ) nbytes = 10; s = alloc_empty_string(len+nbytes); memcpy(val_string(s),val_string(b->buf),len); b->buf = s; } /** utf8_buf_add : 'buf -> int -> void Add a valid UTF8 char (0 - 0x10FFFF) to the buffer **/ static value utf8_buf_add( value buf, value uchar ) { ubuf *b; unsigned char *s; unsigned int c; val_check_kind(buf,k_ubuf); val_check(uchar,int); b = (ubuf*)val_data(buf); c = (unsigned)val_int(uchar); if( c <= 0x7F ) { if( b->pos >= val_strlen(b->buf) ) utf8_buf_resize(b); val_string(b->buf)[b->pos++] = (char)c; return val_true; } if( b->pos + 4 > val_strlen(b->buf) ) utf8_buf_resize(b); s = (unsigned char*)val_string(b->buf); if( c <= 0x7FF ) { b->nesc += 1; s[b->pos++] = 0xC0 | (c >> 6); s[b->pos++] = 0x80 | (c & 63); } else if( c <= 0xFFFF ) { b->nesc += 2; s[b->pos++] = 0xE0 | (c >> 12); s[b->pos++] = 0x80 | ((c >> 6) & 63); s[b->pos++] = 0x80 | (c & 63); } else if( c <= 0x10FFFF ) { b->nesc += 3; s[b->pos++] = 0xF0 | (c >> 18); s[b->pos++] = 0x80 | ((c >> 12) & 63); s[b->pos++] = 0x80 | ((c >> 6) & 63); s[b->pos++] = 0x80 | (c & 63); } else neko_error(); return val_true; } /** utf8_buf_content : 'buf -> string Return the current content of the buffer. This is not a copy of the buffer but the shared content. Retreiving content and then continuing to add chars is possible but not very efficient. **/ static value utf8_buf_content( value buf ) { ubuf *b; val_check_kind(buf,k_ubuf); b = (ubuf*)val_data(buf); val_set_length(b->buf,b->pos); val_string(b->buf)[b->pos] = 0; return b->buf; } /** utf8_buf_length : 'buf -> int Return the number of UTF8 chars stored in the buffer **/ static value utf8_buf_length( value buf ) { ubuf *b; val_check_kind(buf,k_ubuf); b = (ubuf*)val_data(buf); return alloc_int(b->pos - b->nesc); } /** utf8_buf_size : 'buf -> int Return the current size in bytes of the buffer **/ static value utf8_buf_size( value buf ) { ubuf *b; val_check_kind(buf,k_ubuf); b = (ubuf*)val_data(buf); return alloc_int(b->pos); } /** utf8_validate : string -> bool Validate if a string is encoded using the UTF8 format **/ static value utf8_validate( value str ) { int l; unsigned char *s; val_check(str,string); l = val_strlen(str); s = (unsigned char*)val_string(str); while( l-- ) { unsigned char c = *s++; if( c < 0x80 ) continue; else if( c < 0xC0 ) return val_false; else if( c < 0xE0 ) { if( (*s++ & 0x80) != 0x80 ) return val_false; l--; } else if( c < 0xF0 ) { if( (*s++ & 0x80) != 0x80 ) return val_false; if( (*s++ & 0x80) != 0x80 ) return val_false; l-=2; } else { if( (*s++ & 0x80) != 0x80 ) return val_false; if( (*s++ & 0x80) != 0x80 ) return val_false; if( (*s++ & 0x80) != 0x80 ) return val_false; l-=3; } } return val_true; } /** utf8_length : string -> int Returns the number of UTF8 chars in the string. **/ static value utf8_length( value str ) { int l; int count = 0; unsigned char *s; val_check(str,string); l = val_strlen(str); s = (unsigned char*)val_string(str); while( l > 0 ) { unsigned char c = *s; count++; if( c < 0x80 ) { l--; s++; } else if( c < 0xC0 ) neko_error(); else if( c < 0xE0 ) { l-=2; s+=2; } else if( c < 0xF0 ) { l-=3; s+=3; } else { l-=4; s+=4; } } if( l < 0 ) neko_error(); return alloc_int(count); } /** utf8_sub : string -> pos:int -> len:int -> string Returns a part of an UTF8 string. **/ static value utf8_sub( value str, value pos, value len ) { int l; int count; unsigned char *s, *save; val_check(str,string); val_check(pos,int); val_check(len,int); l = val_strlen(str); count = val_int(pos); if( count < 0 ) neko_error(); s = (unsigned char*)val_string(str); while( count-- && l > 0 ) { unsigned char c = *s; if( c < 0x80 ) { l--; s++; } else if( c < 0xC0 ) neko_error(); else if( c < 0xE0 ) { l-=2; s+=2; } else if( c < 0xF0 ) { l-=3; s+=3; } else { l-=4; s+=4; } } if( l < 0 ) neko_error(); save = s; count = val_int(len); if( count < 0 ) neko_error(); while( count-- && l > 0 ) { unsigned char c = *s; if( c < 0x80 ) { l--; s++; } else if( c < 0xC0 ) neko_error(); else if( c < 0xE0 ) { l-=2; s+=2; } else if( c < 0xF0 ) { l-=3; s+=3; } else { l-=4; s+=4; } } if( l < 0 ) neko_error(); l = (int)(s - save); str = alloc_empty_string(l); memcpy(val_string(str),save,l); return str; } /** utf8_get : string -> n:int -> int Returns the [n]th char in an UTF8 string. This might be inefficient if [n] is big. **/ static value utf8_get( value str, value pos ) { int l; int p; unsigned char *s; val_check(pos,int); val_check(str,string); l = val_strlen(str); p = val_int(pos); if( p < 0 ) neko_error(); s = (unsigned char*)val_string(str); while( l-- ) { unsigned char c = *s++; if( c < 0x80 ) { if( p-- == 0 ) return alloc_int(c); } else if( c < 0xC0 ) neko_error(); else if( c < 0xE0 ) { l--; if( l < 0 ) neko_error(); if( p-- == 0 ) return alloc_int(((c & 0x3F) << 6) | ((*s) & 0x7F)); s++; } else if( c < 0xF0 ) { l -= 2; if( l < 0 ) neko_error(); if( p-- == 0 ) return alloc_int(((c & 0x1F) << 12) | (((*s) & 0x7F) << 6) | (s[1] & 0x7F)); s += 2; } else { l -= 3; if( l < 0 ) neko_error(); if( p-- == 0 ) return alloc_int(((c & 0x0F) << 18) | (((*s) & 0x7F) << 12) | ((s[1] & 0x7F) << 6) | (s[2] & 0x7F)); s += 3; } } neko_error(); return val_true; } /** utf8_iter : string -> f:(int -> void) -> void Call [f] with each of UTF8 char of the string. **/ static value utf8_iter( value str, value f ) { int l; unsigned char *s; val_check(str,string); val_check_function(f,1); l = val_strlen(str); s = (unsigned char*)val_string(str); while( l-- ) { unsigned char c = *s++; if( c < 0x80 ) val_call1(f,alloc_int(c)); else if( c < 0xC0 ) neko_error(); else if( c < 0xE0 ) { l--; if( l < 0 ) neko_error(); val_call1(f,alloc_int(((c & 0x3F) << 6) | ((*s) & 0x7F))); s++; } else if( c < 0xF0 ) { l -= 2; if( l < 0 ) neko_error(); val_call1(f,alloc_int(((c & 0x1F) << 12) | (((*s) & 0x7F) << 6) | (s[1] & 0x7F))); s += 2; } else { l -= 3; if( l < 0 ) neko_error(); val_call1(f,alloc_int(((c & 0x0F) << 18) | (((*s) & 0x7F) << 12) | ((s[1] & 0x7F) << 6) | (s[2] & 0x7F))); s += 3; } } return val_true; } /** utf8_compare : s1:string -> s2:string -> int Compare two UTF8 strings according to UTF8 char codes. **/ static value utf8_compare( value str1, value str2 ) { int l1, l2, l; unsigned char *s1, *s2; val_check(str1,string); val_check(str2,string); l1 = val_strlen(str1); l2 = val_strlen(str2); s1 = (unsigned char*)val_string(str1); s2 = (unsigned char*)val_string(str2); l = (l1 < l2)?l1:l2; while( l-- ) { unsigned char c1 = *s1++; unsigned char c2 = *s2++; if( c1 != c2 ) return alloc_int((c1 > c2)?1:-1); if( c1 < 0x7F ) continue; else if( c1 < 0xC0 ) neko_error(); else if( c1 < 0xE0 ) { l--; if( l < 0 ) neko_error(); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?1:-1); } else if( c1 < 0xF0 ) { l-=2; if( l < 0 ) neko_error(); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?1:-1); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?1:-1); } else { l -= 3; if( l < 0 ) neko_error(); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?1:-1); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?1:-1); if( *s1++ != *s2++ ) return alloc_int((s1[-1] > s2[-1])?1:-1); } } if( l1 != l2 ) return alloc_int((l1 > l2)?1:-1); return alloc_int(0); } DEFINE_PRIM(utf8_buf_alloc,1); DEFINE_PRIM(utf8_buf_add,2); DEFINE_PRIM(utf8_buf_content,1); DEFINE_PRIM(utf8_buf_length,1); DEFINE_PRIM(utf8_buf_size,1); DEFINE_PRIM(utf8_get,2); DEFINE_PRIM(utf8_validate,1); DEFINE_PRIM(utf8_iter,2); DEFINE_PRIM(utf8_length,1); DEFINE_PRIM(utf8_compare,2); DEFINE_PRIM(utf8_sub,3); /* ************************************************************************ */ neko-2-4-0/libs/std/xml.c000066400000000000000000000216611464615675700151710ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #ifndef NEKO_WINDOWS # include # undef strcmpi # define strcmpi(a,b) strcasecmp(a,b) #else # include #endif #define ERROR(msg) xml_error(xml,p,line,msg); // -------------- parsing -------------------------- typedef enum { IGNORE_SPACES, BEGIN, BEGIN_NODE, TAG_NAME, BODY, ATTRIB_NAME, EQUALS, ATTVAL_BEGIN, ATTRIB_VAL, CHILDREN, CLOSE, WAIT_END, WAIT_END_RET, PCDATA, HEADER, COMMENT, DOCTYPE, CDATA, } STATE; extern field id_pcdata; extern field id_xml; extern field id_done; extern field id_comment; extern field id_cdata; extern field id_doctype; static void xml_error( const char *xml, const char *p, int *line, const char *msg ) { buffer b = alloc_buffer("Xml parse error : "); int l = (int)strlen(p); int nchars = 30; buffer_append(b,msg); buffer_append(b," at line "); val_buffer(b,alloc_int(*line)); buffer_append(b," : "); if( p != xml ) buffer_append(b,"..."); buffer_append_sub(b,p,(l < nchars)?l:nchars); if( l > nchars ) buffer_append(b,"..."); if( l == 0 ) buffer_append(b,""); bfailure(b); } static bool is_valid_char( int c ) { return ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || ( c >= '0' && c <= '9' ) || c == ':' || c == '.' || c == '_' || c == '-'; } static void do_parse_xml( const char *xml, const char **lp, int *line, value callb, const char *parentname ) { STATE state = BEGIN; STATE next = BEGIN; field aname = (field)0; value attribs = NULL; value nodename = NULL; const char *start = NULL; const char *p = *lp; char c = *p; int nsubs = 0, nbrackets = 0; while( c ) { switch( state ) { case IGNORE_SPACES: switch( c ) { case '\n': case '\r': case '\t': case ' ': break; default: state = next; continue; } break; case BEGIN: switch( c ) { case '<': state = IGNORE_SPACES; next = BEGIN_NODE; break; default: start = p; state = PCDATA; continue; } break; case PCDATA: if( c == '<' ) { val_ocall1(callb,id_pcdata,copy_string(start,p-start)); nsubs++; state = IGNORE_SPACES; next = BEGIN_NODE; } break; case CDATA: if( c == ']' && p[1] == ']' && p[2] == '>' ) { val_ocall1(callb,id_cdata,copy_string(start,p-start)); nsubs++; p += 2; state = BEGIN; } break; case BEGIN_NODE: switch( c ) { case '!': if( p[1] == '[' ) { p += 2; if( (p[0] != 'C' && p[0] != 'c') || (p[1] != 'D' && p[1] != 'd') || (p[2] != 'A' && p[2] != 'a') || (p[3] != 'T' && p[3] != 't') || (p[4] != 'A' && p[4] != 'a') || (p[5] != '[') ) ERROR("Expected ': state = CHILDREN; nsubs++; val_ocall2(callb,id_xml,nodename,attribs); break; default: state = ATTRIB_NAME; start = p; continue; } break; case ATTRIB_NAME: if( !is_valid_char(c) ) { value tmp; if( start == p ) ERROR("Expected attribute name"); tmp = copy_string(start,p-start); aname = val_id(val_string(tmp)); if( !val_is_null(val_field(attribs,aname)) ) ERROR("Duplicate attribute"); state = IGNORE_SPACES; next = EQUALS; continue; } break; case EQUALS: switch( c ) { case '=': state = IGNORE_SPACES; next = ATTVAL_BEGIN; break; default: ERROR("Expected ="); } break; case ATTVAL_BEGIN: switch( c ) { case '"': case '\'': state = ATTRIB_VAL; start = p; break; default: ERROR("Expected \""); } break; case ATTRIB_VAL: if( c == *start ) { value aval = copy_string(start+1,p-start-1); alloc_field(attribs,aname,aval); state = IGNORE_SPACES; next = BODY; } break; case CHILDREN: *lp = p; do_parse_xml(xml,lp,line,callb,val_string(nodename)); p = *lp; start = p; state = BEGIN; break; case WAIT_END: switch( c ) { case '>': val_ocall0(callb,id_done); state = BEGIN; break; default : ERROR("Expected >"); } break; case WAIT_END_RET: switch( c ) { case '>': if( nsubs == 0 ) val_ocall1(callb,id_pcdata,alloc_string("")); val_ocall0(callb,id_done); *lp = p; return; default : ERROR("Expected >"); } break; case CLOSE: if( !is_valid_char(c) ) { if( start == p ) ERROR("Expected node name"); { value v = copy_string(start,p - start); if( strcmpi(parentname,val_string(v)) != 0 ) { buffer b = alloc_buffer("Expected "); ERROR(val_string(buffer_to_string(b))); } } state = IGNORE_SPACES; next = WAIT_END_RET; continue; } break; case COMMENT: if( c == '-' && p[1] == '-' && p[2] == '>' ) { val_ocall1(callb,id_comment,copy_string(start,p-start)); p += 2; state = BEGIN; } break; case DOCTYPE: if( c == '[' ) nbrackets++; else if( c == ']' ) nbrackets--; else if( c == '>' && nbrackets == 0 ) { val_ocall1(callb,id_doctype,copy_string(start,p-start)); state = BEGIN; } break; case HEADER: if( c == '?' && p[1] == '>' ) { p++; val_ocall1(callb,id_comment,copy_string(start,p-start)); state = BEGIN; } break; } c = *++p; if( c == '\n' ) (*line)++; } if( state == BEGIN ) { start = p; state = PCDATA; } if( parentname == NULL && state == PCDATA ) { if( p != start || nsubs == 0 ) val_ocall1(callb,id_pcdata,copy_string(start,p-start)); return; } ERROR("Unexpected end"); } // ---------------------------------------------- /**

Xml

The standard event-driven XML parser.

**/ /** parse_xml : xml:string -> events:object -> void The [parse_xml] parse a string and for each parsed element call the corresponding object method in [events] :
  • [void xml( name : string, attribs : object)] when an XML node is found
  • [void done()] when an XML node is closed
  • [void pcdata(string)] when PCData chars found
  • [void cdata(string)] when a CData session is found
  • [void comment(string)] when some comment or special header is found
You can then implement the events so they build the appropriate XML data structure needed by your language.
**/ static value parse_xml( value str, value callb ) { const char *p; int line = 0; val_check(str,string); val_check(callb,object); p = val_string(str); // skip BOM if( p[0] == (char)0xEF && p[1] == (char)0xBB && p[2] == (char)0xBF ) p += 3; do_parse_xml(p,&p,&line,callb,NULL); return val_true; } DEFINE_PRIM(parse_xml,2); /* ************************************************************************ */ neko-2-4-0/libs/ui/000077500000000000000000000000001464615675700140425ustar00rootroot00000000000000neko-2-4-0/libs/ui/CMakeLists.txt000066400000000000000000000011611464615675700166010ustar00rootroot00000000000000include(FindPkgConfig) ###################### # ui.ndll add_library(ui.ndll MODULE ui.c) target_link_libraries(ui.ndll libneko) if(APPLE) find_library(CARBON_LIBRARY Carbon REQUIRED) target_link_libraries(ui.ndll ${CARBON_LIBRARY}) elseif(UNIX) pkg_check_modules(GTK3 REQUIRED gtk+-3.0) target_include_directories(ui.ndll PRIVATE ${GTK3_INCLUDEDIR} ${GTK3_INCLUDE_DIRS} ) target_link_libraries(ui.ndll ${GTK3_LIBRARIES}) endif() set_target_properties(ui.ndll PROPERTIES PREFIX "" OUTPUT_NAME ui SUFFIX .ndll ) install ( TARGETS ui.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/ui/ui.c000066400000000000000000000152221464615675700146250ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #define HEADER_IMPORTS #include #include #if defined(NEKO_WINDOWS) # include # define CLASS_NAME "Neko_UI_wnd_class" # define WM_SYNC_CALL (WM_USER + 101) #elif defined(NEKO_MAC) # undef lock_acquire # undef lock_release # undef lock_try # include # include # define UIEvent 0xFFFFAA00 # define eCall 0x0 enum { pFunc = 'func' }; extern void RunApplicationEventLoop(void); extern void QuitApplicationEventLoop(void); #else # include # include # include # include #endif /**

UI

Core native User Interface support. This API uses native WIN32 API on Windows, Carbon API on OSX, and GTK3 on Linux.

**/ typedef struct { int init_done; #if defined(NEKO_WINDOWS) DWORD tid; HWND wnd; #elif defined(NEKO_MAC) pthread_t tid; #else pthread_t tid; pthread_mutex_t lock; #endif } ui_data; static ui_data data = { 0 }; #if defined(NEKO_WINDOWS) static LRESULT CALLBACK WindowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { switch( msg ) { case WM_SYNC_CALL: { value *r = (value*)lparam; value f = *r; free_root(r); // There are some GC issues here when having a lot of threads // It seems that somehow the function is not called, it might // also trigger some crashes. val_call0(f); return 0; }} return DefWindowProc(hwnd,msg,wparam,lparam); } #elif defined(NEKO_MAC) static OSStatus nothing() { return 0; } static OSStatus handleEvents( EventHandlerCallRef ref, EventRef e, void *data ) { switch( GetEventKind(e) ) { case eCall: { value *r; value f; GetEventParameter(e,pFunc,typeVoidPtr,0,sizeof(void*),0,&r); f = *r; free_root(r); val_call0(f); break; }} return 0; } #elif defined(NEKO_LINUX) static gint onSyncCall( gpointer data ) { value *r = (value*)data; value f = *r; free_root(r); val_call0(f); return FALSE; } #endif DEFINE_ENTRY_POINT(ui_main); void ui_main() { if( data.init_done ) return; data.init_done = 1; # if defined(NEKO_WINDOWS) { WNDCLASSEX wcl; HINSTANCE hinst = GetModuleHandle(NULL); memset(&wcl,0,sizeof(wcl)); wcl.cbSize = sizeof(WNDCLASSEX); wcl.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wcl.lpfnWndProc = WindowProc; wcl.cbClsExtra = 0; wcl.cbWndExtra = 0; wcl.hInstance = hinst; wcl.hIcon = NULL; wcl.hCursor = LoadCursor(NULL, IDC_ARROW); wcl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); wcl.lpszMenuName = ""; wcl.lpszClassName = CLASS_NAME; wcl.hIconSm = 0; RegisterClassEx(&wcl); } data.tid = GetCurrentThreadId(); data.wnd = CreateWindow(CLASS_NAME,"",0,0,0,0,0,NULL,NULL,NULL,NULL); # elif defined(NEKO_MAC) MPCreateTask(nothing,NULL,0,0,0,0,0,NULL); // creates a MPTask that will enable Carbon MT data.tid = pthread_self(); EventTypeSpec ets = { UIEvent, eCall }; InstallEventHandler(GetApplicationEventTarget(),NewEventHandlerUPP(handleEvents),1,&ets,0,0); # elif defined(NEKO_LINUX) gdk_threads_init(); gtk_init(NULL,NULL); setlocale(LC_NUMERIC,"POSIX"); // prevent broking atof() data.tid = pthread_self(); pthread_mutex_init(&data.lock,NULL); # endif } /** ui_is_main : void -> bool Tells if the current thread is the main loop thread or not. The main loop thread is the one in which the first "ui" library primitive has been loaded. **/ static value ui_is_main() { # ifdef NEKO_WINDOWS return alloc_bool(data.tid == GetCurrentThreadId()); # else return alloc_bool(pthread_equal(data.tid,pthread_self())); # endif } /** ui_loop : void -> void Starts the native UI event loop. This method can only be called from the main thread. **/ static value ui_loop() { if( !val_bool(ui_is_main()) ) neko_error(); # if defined(NEKO_WINDOWS) { MSG msg; while( GetMessage(&msg,NULL,0,0) ) { TranslateMessage(&msg); DispatchMessage(&msg); if( msg.message == WM_QUIT ) break; } } # elif defined(NEKO_MAC) RunApplicationEventLoop(); # else gtk_main(); # endif return val_null; } /** ui_stop_loop : void -> void Stop the native UI event loop. This method can only be called from the main thread. **/ static value ui_stop_loop() { if( !val_bool(ui_is_main()) ) neko_error(); # if defined(NEKO_WINDOWS) while( !PostMessage(data.wnd,WM_QUIT,0,0) ) Sleep(100); # elif defined(NEKO_MAC) QuitApplicationEventLoop(); # else gtk_main_quit(); # endif return val_null; } /** ui_sync : callb:(void -> void) -> void Queue a method call [callb] to be executed by the main thread while running the UI event loop. This can be used to perform UI updates in the UI thread using results processed by another thread. **/ static value ui_sync( value f ) { value *r; val_check_function(f,0); r = alloc_root(1); *r = f; # if defined(NEKO_WINDOWS) while( !PostMessage(data.wnd,WM_SYNC_CALL,0,(LPARAM)r) ) Sleep(100); # elif defined(NEKO_MAC) EventRef e; CreateEvent(NULL,UIEvent,eCall,GetCurrentEventTime(),kEventAttributeUserEvent,&e); SetEventParameter(e,pFunc,typeVoidPtr,sizeof(void*),&r); PostEventToQueue(GetMainEventQueue(),e,kEventPriorityStandard); ReleaseEvent(e); # elif defined(NEKO_LINUX) // the lock should not be needed because GTK is MT-safe // however the GTK lock mechanism is a LOT slower than // using a pthread_mutex pthread_mutex_lock(&data.lock); gdk_threads_add_timeout( 0, onSyncCall, (gpointer)r ); pthread_mutex_unlock(&data.lock); # endif return val_null; } DEFINE_PRIM(ui_loop,0); DEFINE_PRIM(ui_stop_loop,0); DEFINE_PRIM(ui_is_main,0); DEFINE_PRIM(ui_sync,1); /* ************************************************************************ */ neko-2-4-0/libs/zlib/000077500000000000000000000000001464615675700143655ustar00rootroot00000000000000neko-2-4-0/libs/zlib/CMakeLists.txt000066400000000000000000000007001464615675700171220ustar00rootroot00000000000000 ###################### # zlib.ndll add_library(zlib.ndll MODULE zlib.c) if(STATIC_ZLIB) add_dependencies(zlib.ndll Zlib) endif() target_include_directories(zlib.ndll PRIVATE ${ZLIB_INCLUDE_DIRS}) target_link_libraries(zlib.ndll libneko ${ZLIB_LIBRARIES}) set_target_properties(zlib.ndll PROPERTIES PREFIX "" OUTPUT_NAME zlib SUFFIX .ndll ) install ( TARGETS zlib.ndll DESTINATION ${DEST_NDLL} ) install(SCRIPT ${NEKO_FLATTEN_SCRIPT}) neko-2-4-0/libs/zlib/zlib.c000066400000000000000000000205671464615675700155030ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include /**

ZLib

Give access to the popular ZLib compression library, used in several file formats such as ZIP and PNG.

**/ DEFINE_KIND(k_stream_def); DEFINE_KIND(k_stream_inf); #define val_stream(v) ((z_stream *)val_data(v)) #define val_flush(s) *((int*)(((char*)s)+sizeof(z_stream))) static field id_read, id_write, id_done; DEFINE_ENTRY_POINT(zlib_main); void zlib_main() { id_read = val_id("read"); id_write = val_id("write"); id_done = val_id("done"); } static void free_stream_def( value v ) { z_stream *s = val_stream(v); deflateEnd(s); // no error free(s); val_kind(v) = NULL; val_gc(v,NULL); } static void free_stream_inf( value v ) { z_stream *s = val_stream(v); inflateEnd(s); // no error free(s); val_kind(v) = NULL; val_gc(v,NULL); } static void zlib_error( z_stream *z, int err ) { buffer b = alloc_buffer("ZLib Error : "); if( z && z->msg ) { buffer_append(b,z->msg); buffer_append(b," ("); } val_buffer(b,alloc_int(err)); if( z && z->msg ) buffer_append_char(b,')'); val_throw(buffer_to_string(b)); } /** deflate_init : level:int -> 'dstream Open a compression stream with the given level of compression **/ static value deflate_init( value level ) { z_stream *z; value s; int err; val_check(level,int); z = (z_stream*)malloc(sizeof(z_stream) + sizeof(int)); memset(z,0,sizeof(z_stream)); val_flush(z) = Z_NO_FLUSH; if( (err = deflateInit(z,val_int(level))) != Z_OK ) { free(z); zlib_error(NULL,err); } s = alloc_abstract(k_stream_def,z); val_gc(s,free_stream_def); return s; } /** deflate_buffer : 'dstream -> src:string -> srcpos:int -> dst:string -> dstpos:int -> { done => bool, read => int, write => int } **/ static value deflate_buffer( value s, value src, value srcpos, value dst, value dstpos ) { z_stream *z; int slen, dlen, err; value o; val_check_kind(s,k_stream_def); val_check(src,string); val_check(srcpos,int); val_check(dst,string); val_check(dstpos,int); z = val_stream(s); if( val_int(srcpos) < 0 || val_int(dstpos) < 0 ) neko_error(); slen = val_strlen(src) - val_int(srcpos); dlen = val_strlen(dst) - val_int(dstpos); if( slen < 0 || dlen < 0 ) neko_error(); z->next_in = (Bytef*)(val_string(src) + val_int(srcpos)); z->next_out = (Bytef*)(val_string(dst) + val_int(dstpos)); z->avail_in = slen; z->avail_out = dlen; if( (err = deflate(z,val_flush(z))) < 0 ) zlib_error(z,err); z->next_in = NULL; z->next_out = NULL; o = alloc_object(NULL); alloc_field(o,id_done,alloc_bool(err == Z_STREAM_END)); alloc_field(o,id_read,alloc_int(slen - z->avail_in)); alloc_field(o,id_write,alloc_int(dlen - z->avail_out)); return o; } /** deflate_end : 'dstream -> void Close a compression stream **/ static value deflate_end( value s ) { val_check_kind(s,k_stream_def); free_stream_def(s); return val_null; } /** inflate_init : window_size:int? -> 'istream Open a decompression stream **/ static value inflate_init( value wsize ) { z_stream *z; value s; int err; int wbits; if( val_is_null(wsize) ) wbits = MAX_WBITS; else { val_check(wsize,int); wbits = val_int(wsize); } z = (z_stream*)malloc(sizeof(z_stream) + sizeof(int)); memset(z,0,sizeof(z_stream)); val_flush(z) = Z_NO_FLUSH; if( (err = inflateInit2(z,wbits)) != Z_OK ) { free(z); zlib_error(NULL,err); } s = alloc_abstract(k_stream_inf,z); val_gc(s,free_stream_inf); return s; } /** inflate_buffer : 'istream -> src:string -> srcpos:int -> dst:string -> dstpos:int -> { done => bool, read => int, write => int } **/ static value inflate_buffer( value s, value src, value srcpos, value dst, value dstpos ) { z_stream *z; int slen, dlen, err; value o; val_check_kind(s,k_stream_inf); val_check(src,string); val_check(srcpos,int); val_check(dst,string); val_check(dstpos,int); z = val_stream(s); if( val_int(srcpos) < 0 || val_int(dstpos) < 0 ) neko_error(); slen = val_strlen(src) - val_int(srcpos); dlen = val_strlen(dst) - val_int(dstpos); if( slen < 0 || dlen < 0 ) neko_error(); z->next_in = (Bytef*)(val_string(src) + val_int(srcpos)); z->next_out = (Bytef*)(val_string(dst) + val_int(dstpos)); z->avail_in = slen; z->avail_out = dlen; if( (err = inflate(z,val_flush(z))) < 0 ) zlib_error(z,err); z->next_in = NULL; z->next_out = NULL; o = alloc_object(NULL); alloc_field(o,id_done,alloc_bool(err == Z_STREAM_END)); alloc_field(o,id_read,alloc_int(slen - z->avail_in)); alloc_field(o,id_write,alloc_int(dlen - z->avail_out)); return o; } /** inflate_end : 'istream -> void Close a decompression stream **/ static value inflate_end( value s ) { val_check_kind(s,k_stream_inf); free_stream_inf(s); return val_null; } /** set_flush_mode : 'stream -> string -> void Change the flush mode ("NO","SYNC","FULL","FINISH","BLOCK") **/ static value set_flush_mode( value s, value flush ) { int f; if( !val_is_kind(s,k_stream_inf) ) val_check_kind(s,k_stream_def); val_check(flush,string); if( strcmp(val_string(flush),"NO") == 0 ) f = Z_NO_FLUSH; else if( strcmp(val_string(flush),"SYNC") == 0 ) f = Z_SYNC_FLUSH; else if( strcmp(val_string(flush),"FULL") == 0 ) f = Z_FULL_FLUSH; else if( strcmp(val_string(flush),"FINISH") == 0 ) f = Z_FINISH; else if( strcmp(val_string(flush),"BLOCK") == 0 ) f = Z_BLOCK; else neko_error(); val_flush(val_stream(s)) = f; return val_null; } /** get_adler32 : 'stream -> 'int32 Returns the adler32 value of the stream **/ static value get_adler32( value s ) { if( !val_is_kind(s,k_stream_inf) ) val_check_kind(s,k_stream_def); return alloc_int32(val_stream(s)->adler); } /** update_adler32 : adler:'int32 -> string -> pos:int -> len:int -> 'int32 Update an adler32 value with a substring **/ static value update_adler32( value adler, value s, value pos, value len ) { val_check(adler,int32); val_check(s,string); val_check(pos,int); val_check(len,int); if( val_int(pos) < 0 || val_int(len) < 0 || val_int(pos) + val_int(len) > val_strlen(s) ) neko_error(); return alloc_int32(adler32(val_int32(adler),(Bytef*)(val_string(s)+val_int(pos)),val_int(len))); } /** update_crc32 : crc:'int32 -> string -> pos:int -> len:int -> 'int32 Update a CRC32 value with a substring **/ static value update_crc32( value crc, value s, value pos, value len ) { val_check(crc,int32); val_check(s,string); val_check(pos,int); val_check(len,int); if( val_int(pos) < 0 || val_int(len) < 0 || val_int(pos) + val_int(len) > val_strlen(s) ) neko_error(); return alloc_int32(crc32(val_int32(crc),(Bytef*)(val_string(s)+val_int(pos)),val_int(len))); } /** deflate_bound : 'dstream -> n:int -> int Return the maximum buffer size needed to write [n] bytes **/ static value deflate_bound( value s, value size ) { val_check_kind(s,k_stream_def); val_check(size,int); return alloc_int(deflateBound(val_stream(s),val_int(size))); } DEFINE_PRIM(deflate_init,1); DEFINE_PRIM(deflate_buffer,5); DEFINE_PRIM(deflate_end,1); DEFINE_PRIM(inflate_init,1); DEFINE_PRIM(inflate_buffer,5); DEFINE_PRIM(inflate_end,1); DEFINE_PRIM(set_flush_mode,2); DEFINE_PRIM(deflate_bound,2); DEFINE_PRIM(get_adler32,1); DEFINE_PRIM(update_adler32,4); DEFINE_PRIM(update_crc32,4); /* ************************************************************************ */ neko-2-4-0/src/000077500000000000000000000000001464615675700132635ustar00rootroot00000000000000neko-2-4-0/src/benchs/000077500000000000000000000000001464615675700145255ustar00rootroot00000000000000neko-2-4-0/src/benchs/ackerman.neko000066400000000000000000000004531464615675700171660ustar00rootroot00000000000000ack = function(x,y) { if( x == 0 ) return y + 1; if( y == 0 ) return ack(x-1,1); return ack(x-1,ack(x,y-1)); }; var arg1 = $int($loader.args[1]); var arg2 = $int($loader.args[0]); if( arg1 == null ) arg1 = 3; if( arg2 == null ) arg2 = 4; $print("Ack(",arg1,",",arg2,"): ",ack(arg1,arg2),"\n"); neko-2-4-0/src/benchs/binary-trees.neko000066400000000000000000000020731464615675700200110ustar00rootroot00000000000000/* The Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Nicolas Cannasse */ make = function(i,d) { if( d == 0 ) return $array(i,null,null); var i2 = 2 * i; d -= 1; $array(i,make(i2-1,d),make(i2,d)); }; check = function(n) { if( n[1] == null ) return n[0]; return n[0] + check(n[1]) - check(n[2]); } var arg = $int($loader.args[0]); if( arg == null ) arg = 10; var min_depth = 4; var max_depth = if( min_depth + 2 < arg ) arg else min_depth + 2; var stretch_depth = max_depth + 1 var c = check(make(0,stretch_depth)); $print("stretch tree of depth ",stretch_depth,"\t check: ",c,"\n"); var long_lived_tree = make(0,max_depth); loop_depths = function(d) { if( d <= max_depth ) { var niter = 1 << (max_depth - d + min_depth); var c = 0; var i = 0; while( i < niter ) { i += 1; c += check(make(i,d))+check(make(0-i,d)); } $print(2*niter,"\t trees of depth ",d,"\t check: ",c,"\n"); loop_depths(d + 2); } } loop_depths(min_depth); $print("long lived tree of depth ",max_depth,"\t check: ",check(long_lived_tree),"\n"); neko-2-4-0/src/benchs/fannkuch.neko000066400000000000000000000022501464615675700171770ustar00rootroot00000000000000/* The Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Nicolas Cannasse */ var n = $int($loader.args[0]); if( n == null ) n = 7; check = 0; perm = $amake(n); perm1 = $amake(n); count = $amake(n); maxPerm = $amake(n); maxflips = 0; m = n - 1; r = n; i = 0; while( i < n ) { perm1[i] = i; i += 1 }; while (true) { if( check < 30 ) { i = 0; while( i < n ) { $print(perm1[i]+1); i += 1; }; $print("\n"); check += 1; } while( r != 1 ) { count[r-1] = r; r -= 1; }; if( perm1[0] != 0 && perm1[m] != m ) { i = 0; while( i < n ) { perm[i] = perm1[i]; i += 1; }; flips = 0; k = perm[0]; do { i = 1; j = k - 1; while( i < j ) { tmp = perm[i]; perm[i] = perm[j]; perm[j] = tmp; i += 1; j -= 1; } flips += 1; j = perm[k]; perm[k] = k; k = j; } while( k != 0 ); if( flips > maxflips ) maxflips = flips; } while (true) { if( r == n ) { $print("Pfannkuchen(",n,") = ",maxflips,"\n"); $goto(exit); } perm0 = perm1[0]; i = 0; while( i < r ) { j = i + 1; perm1[i] = perm1[j]; i = j; } perm1[r] = perm0; count[r] -= 1; if( count[r] > 0 ) break; r += 1; } } exit: neko-2-4-0/src/benchs/fib.neko000066400000000000000000000002631464615675700161440ustar00rootroot00000000000000fib = function(n) { if( n <= 1 ) return 1; return fib(n-1) + fib(n-2); }; var arg = $int($loader.args[0]); if( arg == null ) arg = 30; $print("fib(",arg,") = ",fib(arg),"\n"); neko-2-4-0/src/benchs/fp.neko000066400000000000000000000002301464615675700160030ustar00rootroot00000000000000var x = 1.0; var y = 1.0; var i = $float($loader.args[0]); while( i > 0.0 ) { x *= 1.000000001; y /= 1.000000001; i -= 1.0; } $print(x,"\n",y,"\n"); neko-2-4-0/src/benchs/module.neko000066400000000000000000000003001464615675700166610ustar00rootroot00000000000000var arg = $int($loader.args[0]); if( arg == null ) arg = 100; $loader.args = $array(); while( arg > 0 ) { $loader.cache = $new(null); $loader.loadmodule("boot/nekoc.n",$loader); arg -= 1; }neko-2-4-0/src/benchs/nbodies.neko000066400000000000000000000070771464615675700170410ustar00rootroot00000000000000/* The Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Nicolas Cannasse */ pi = 3.141592653589793; solar_mass = (4 * pi * pi); days_per_year = 365.24; sqrt = $loader.loadprim("std@math_sqrt",1); round = $loader.loadprim("std@math_round",1); advance = function(nbodies,bodies,dt) { var i = 0,j; while( i < nbodies ) { var b = bodies[i]; i += 1; j = i; while( j < nbodies ) { var b2 = bodies[j]; var dx = b.x - b2.x; var dy = b.y - b2.y; var dz = b.z - b2.z; var dist = sqrt(dx*dx+dy*dy+dz*dz); var mag = dt / (dist * dist * dist); var bm = b.mass * mag; var b2m = b2.mass * mag; b.vx -= dx * b2m; b.vy -= dy * b2m; b.vz -= dz * b2m; b2.vx += dx * bm; b2.vy += dy * bm; b2.vz += dz * bm; j += 1; } } i = 0; while( i < nbodies ) { var b = bodies[i]; b.x += dt * b.vx; b.y += dt * b.vy; b.z += dt * b.vz; i += 1; } } energy = function(nbodies,bodies) { var e = 0, i = 0, j; while( i < nbodies ) { var b = bodies[i]; e += 0.5 * b.mass * (b.vx * b.vx + b.vy * b.vy + b.vz * b.vz); i += 1; j = i; while( j < nbodies ) { var b2 = bodies[j]; var dx = b.x - b2.x; var dy = b.y - b2.y; var dz = b.z - b2.z; var dist = sqrt(dx*dx+dy*dy+dz*dz); e -= (b.mass * b2.mass) / dist; j += 1; } } return e; }; offset_momentum = function(nbodies,bodies) { var px = 0, py = 0, pz = 0; var i = 0; while( i < nbodies ) { var b = bodies[i]; px += b.vx * b.mass; py += b.vy * b.mass; pz += b.vz * b.mass; i += 1; } var b = bodies[0]; b.vx = 0 - px / solar_mass; b.vy = 0 - py / solar_mass; b.vz = 0 - pz / solar_mass; } ; var bodies = $array( // sun { x => 0, y => 0, z => 0, vx => 0, vy => 0, vz => 0, mass => solar_mass }, // jupiter { x => $float("4.84143144246472090e+00"), y => $float("-1.16032004402742839e+00"), z => $float("-1.03622044471123109e-01"), vx => $float("1.66007664274403694e-03") * days_per_year, vy => $float("7.69901118419740425e-03") * days_per_year, vz => $float("-6.90460016972063023e-05") * days_per_year, mass => $float("9.54791938424326609e-04") * solar_mass }, // saturn { x => $float("8.34336671824457987e+00"), y => $float("4.12479856412430479e+00"), z => $float("-4.03523417114321381e-01"), vx => $float("-2.76742510726862411e-03") * days_per_year, vy => $float("4.99852801234917238e-03") * days_per_year, vz => $float("2.30417297573763929e-05") * days_per_year, mass => $float("2.85885980666130812e-04") * solar_mass }, // uranus { x => $float("1.28943695621391310e+01"), y => $float("-1.51111514016986312e+01"), z => $float("-2.23307578892655734e-01"), vx => $float("2.96460137564761618e-03") * days_per_year, vy => $float("2.37847173959480950e-03") * days_per_year, vz => $float("-2.96589568540237556e-05") * days_per_year, mass => $float("4.36624404335156298e-05") * solar_mass }, // neptune { x => $float("1.53796971148509165e+01"), y => $float("-2.59193146099879641e+01"), z => $float("1.79258772950371181e-01"), vx => $float("2.68067772490389322e-03") * days_per_year, vy => $float("1.62824170038242295e-03") * days_per_year, vz => $float("-9.51592254519715870e-05") * days_per_year, mass => $float("5.15138902046611451e-05") * solar_mass } ); var nbodies = $asize(bodies); display = function() { var prec = $float("1e+09"); var e = energy(nbodies,bodies) * prec; $print(round(e)/prec,"\n"); } var n = $int($loader.args[0]); if( n == null ) n = 1000; offset_momentum(nbodies, bodies); display(); var i = 0; while( i < n ) { advance(nbodies,bodies,0.01); i += 1; }; display(); neko-2-4-0/src/benchs/nsieve.neko000066400000000000000000000011411464615675700166710ustar00rootroot00000000000000/* The Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Nicolas Cannasse */ fmt = function(i) { var s = $string(i); while( $ssize(s) < 8 ) s = " "+s; return s; } nsieve = function(m) { var a = $amake(m); var count = 0; var i = 2; while( i < m ) { if $not(a[i]) { count += 1; var j = (i << 1); while( j < m ) { if( $not(a[j]) ) a[j] = true; j += i; } } i += 1; } $print("Primes up to ",fmt(m)," ",fmt(count),"\n"); } var n = $int($loader.args[0]); if( n == null ) n = 2; var i = 0; while( i < 3 ) { nsieve(10000 << (n - i)); i += 1; } neko-2-4-0/src/benchs/recursive.neko000066400000000000000000000016711464615675700174170ustar00rootroot00000000000000/* The Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Nicolas Cannasse */ ack = function(x,y) { if( x == 0 ) return y + 1; if( y == 0 ) return ack(x - 1,1); return ack(x - 1,ack(x,y - 1)); } fib = function(n) { if( n < 2 ) return 1; return fib(n-2) + fib(n-1); } fib_fp = function(n) { if( n < 2.0 ) return 1.0; return fib_fp(n - 2.0) + fib_fp(n - 1.0); } tak = function(x,y,z) { if( y >= x ) return z; return tak(tak(x-1,y,z),tak(y-1,z,x),tak(z-1,x,y)); } tak_fp = function(x,y,z) { if( y >= x ) return z; return tak_fp(tak_fp(x-1.0,y,z),tak_fp(y-1.0,z,x),tak_fp(z-1.0,x,y)); } var n = $int($loader.args[0]); if( n == null ) n = 4; n -= 1; $print("Ack(3,",n + 1,"): ",ack(3,n + 1),"\n"); $print("Fib(",n + 28.0,".0): ",fib_fp(n + 28.0),".0\n"); $print("Tak(",3*n,",",2*n,",",n,"): ",tak(3*n,2*n,n),"\n"); $print("Fib(",3,"): ",fib(3),"\n"); $print("Tak(3.0,2.0,1.0): ",tak_fp(3.0,2.0,1.0),".0\n"); neko-2-4-0/src/benchs/startup.neko000066400000000000000000000002101464615675700170760ustar00rootroot00000000000000/* The Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Nicolas Cannasse */ $print("hello world\n"); neko-2-4-0/src/benchs/test_ops.neko000066400000000000000000000022531464615675700172450ustar00rootroot00000000000000 eq = function(a,b) { if( a != b ) $print(a," should be ",b,"\n") else $print(".") } eq($string(1),"1"); eq($string(0xFFFFFFFF),"-1"); o = $new(null); o.__add = function(x) return x + 66; o.__radd = function(x) return x + 77; o.__sub = function(x) return x - 66; o.__rsub = function(x) return x - 77; var small = 0x7FFF; var med = 0x7FFF0000; var less = 0x80000000; var spe1 = 0xC0000000; eq($string(small),"32767"); eq($string(med),"2147418112"); eq($string(less),"-2147483648"); eq($string(spe1),"-1073741824"); $print("\n"); eq(small<<16,med); eq(small<<17,-131072); eq(med+0x10000,less); var low = 0xC000; eq(low<<16,spe1); var low = 0xA000; eq(low<<16,0xA0000000); eq(small*small,1073676289); eq(small*small*small,1073840127); eq(small*small*small*med,65536); eq(med+med,-131072); eq(o + 1, 67); eq(1 + o, 78); eq(o + "x", "x66"); eq("x" + o, "x77"); eq( med + 1.1, 2147418113.1 ); eq( 1.1 + med, 2147418113.1 ); eq( 1 - 1.25, -0.25 ); eq( 1.25 - 1, 0.25 ); eq( med - 1, 2147418111 ); eq( 1 - med, -2147418111 ); eq( med - 1.25, 2147418110.75 ); eq( 1.25 - med, -2147418110.75 ); eq( o - 1, -65 ); eq( 1 - o, -76 ); neko-2-4-0/src/core/000077500000000000000000000000001464615675700142135ustar00rootroot00000000000000neko-2-4-0/src/core/Args.nml000066400000000000000000000045121464615675700156210ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type argtype { Void : void -> void; String : string -> void; Int : int -> void; } exception Invalid; function help(head,decl) { print head; print "\n Options :\n"; List.iter (function((name,_,help)) { printf " %s %s\n" (name,help); }) decl; Sys.exit(1) } function parse_args(args,head,decl,def) { var i = &0; var l = Array.length args; var h = Hashtbl.create(); var hdecl = Void (function() { help head decl }); Hashtbl.add h "-help" hdecl; Hashtbl.add h "--help" hdecl; List.iter (function((name,decl,_)) { Hashtbl.add h name decl; }) decl; try { while *i < l { var arg = args.[*i]; i := *i + 1; var d = try { Hashtbl.find h arg; } catch { Not_found -> Void (function() { def arg }) }; match d { | Void f -> f() | String f -> if *i == l then throw Invalid; var param = args.[*i]; i := *i + 1; f param | Int f -> if *i == l then throw Invalid; var param = args.[*i]; i := *i + 1; var pval = try { int param } catch { _ -> throw Invalid }; f pval } } } catch { Invalid -> var arg = args.[*i - 1]; print "Invalid argument : "; print arg; print "\n"; help head decl } } function parse(head,decl,def) { parse_args(Sys.args(),head,decl,def); } neko-2-4-0/src/core/Array.nml000066400000000000000000000074461464615675700160140ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ function string( a : 'a array ) : string { neko(" var s = '['; var items = a[0]; var l = a[2]; var i = 0; while i < l { s = s + @Core.string(items[i]); i = i + 1; if i < l s = s + ', '; } s = s + ']'; s "); }; neko(" @make = function(a) { $array(a,string,$asize(a)); }; @merge_sort = $loader.loadprim('std@merge_sort',3); $exports.@make = @make; "); function length( a : 'a array ) : int { neko("a[2]"); } function create() : 'a array { neko("$array($array(),string,0)"); } function add( a : 'a array, x : 'a ) : void { neko(" var k = a[0]; var n = a[2]; if( $asize(k) == n ) { k = $amake(n*2+1); $ablit(k,0,a[0],0,n); a[0] = k; } k[n] = x; a[2] = n + 1; "); } function get( a : 'a array, p : int ) : 'a { a.[p] } function set( a : 'a array, p : int, x : 'a ) : void { a.[p] := x; } function make( size : int, x : 'a ) : 'a array { neko(" var a = $amake(size); var i = 0; while( i < size ) { a[i] = x; i = i + 1; } $array(a,string,size) "); } function init( size : int, f : int -> 'a ) : 'a array { neko(" var a = $amake(size); var i = 0; while( i < size ) { a[i] = $apply(f,i); i = i + 1; } $array(a,string,size) "); } function iter( f : 'a -> void, a : 'a array ) : void { neko(" var l = a[2]; var i = 0; a = a[0]; while (i < l) { f(a[i]); i = i + 1; } "); } function iteri( f : int -> 'a -> void, a : 'a array ) : void { neko(" var l = a[2]; var i = 0; a = a[0]; while (i < l) { f(i,a[i]); i = i + 1; } "); } function map( f : 'a -> 'b, a : 'a array ) : 'b array { neko(" var l = a[2]; var i = 0; var a2 = $amake(l); a = a[0]; while i < l { a2[i] = $apply(f,a[i]); i = i + 1; } $array(a2,string,l); "); } function sort( f : 'a -> 'a -> int, a : 'a array ) : void { neko(" @merge_sort(a[0],a[2],f) "); } function list( a : 'a array ) : 'a list { neko(" var i = a[2]; var l = @Core.@empty; a = a[0]; while i > 0 { i = i - 1; l = @Core.@cons(a[i],l); } l "); } function append( src : 'a array, dst : 'a array ) : void { neko(" var l = src[2]; var i = 0; src = src[0]; while i < l { add(dst,src[i]); i = i + 1; } "); } function sub( a : 'a array, p : int, l : int ) : 'a array { neko(" var al = a[2]; if( p + l > al ) $throw('$asub'); $array($asub(a[0],p,l),string,l) "); } function blit( dst : 'a array, pdst : int, src : 'a array, psrc : int, len : int ) { neko(" $ablit(dst[0],pdst,src[0],psrc,len) "); } function index( a : 'a array, it : 'a ) : int { neko(" var al = a[2]; a = a[0]; var i = 0; while( i < al ) { if( @Core.@compare(a[i],it) == 0 ) return i; i = i + 1; } "); throw Not_found } neko-2-4-0/src/core/Buffer.nml000066400000000000000000000035001464615675700161320ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type t; neko(" @buffer_new = $loader.loadprim('std@buffer_new',0); @buffer_add = $loader.loadprim('std@buffer_add',2); @buffer_add_sub = $loader.loadprim('std@buffer_add_sub',4); @buffer_add_char = $loader.loadprim('std@buffer_add_char',2); @buffer_string = $loader.loadprim('std@buffer_string',1); @buffer_reset = $loader.loadprim('std@buffer_reset',1); "); function create() : t { neko("@buffer_new()"); } function add(b:t,x : 'a) : void { neko("@buffer_add(b,x)"); } function reset(b:t) : void { neko("@buffer_reset(b)"); } var string : t -> string = neko("@buffer_string"); var add_sub : t -> string -> int -> int -> void = neko("@buffer_add_sub"); var add_char : t -> char -> void = neko("@buffer_add_char"); neko-2-4-0/src/core/Core.nml000066400000000000000000000141561464615675700156220ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type void; type int; type int32; type float; type char; type string; type error; type 'a array; type 'a ref; type 'a format; type 'a stream; type 'a option { None; Some : 'a; } type neko_value; exception Neko_error : neko_value; exception Invalid_argument : string; exception Assert_failure : (string,int); exception Error : string; exception Stream_error; exception Not_found; exception Exit; /* ----- theses are defined directly in the compiler because they need additional magic. type bool { true; false; } type 'a list { []; :: : ('a , 'a list); } val neko : string -> 'a ----- */ neko(" @sprintf = $loader.loadprim('std@sprintf',2); @string = function(x) { // tuples if( $typeof(x) == $tarray ) { // constructor var printer = x[1]; if( $typeof(printer) == $tfunction && $nargs(printer) == 1 ) return printer(x); var s = '('; var l = $asize(x); var i = 0; while i < l { s = s + @string(x[i]); i = i + 1; if i < l s = s + ', '; } s = s + ')'; return s; } // others return $string(x); } $exports.@print_record = function() { var o = $objfields(this); var s = '{ '; var i = 0; var l = $asize(o) - 1; while i < l { var f = o[i]; var fname = $field(f); if fname == '__string' l = l + 1; else s = s + fname + ' = ' + @string($objget(this,f))+'; '; i = i + 1; } s = s + '}'; s } $exports.@print_union = function(s,a) { var l = $asize(a); if l == 2 return s; var i = 2; s = s + '('; while i < l { s = s + @string(a[i]); i = i + 1; if i < l s = s + ', '; } s = s + ')'; return s; } @compare = function(a,b,history) { if( $pcompare(a,b) == 0 ) return 0; if( $typeof(a) == $tarray ) { var l1 = $asize(a); var l2 = $asize(b); if( l1 != l2 ) return $compare(l1,l2); var h = history; while h != null { if( h[0] == a && h[1] == b ) return $pcompare(a,b); h = h[2]; } var i = 0; while i < l1 { var k = @compare(a[i],b[i],$array(a,b,history)); if( k != 0 ) return k; i = i + 1; } return 0; } if( $typeof(a) == $tobject ) { var h = history; while h != null { if( h[0] == a && h[1] == b ) return $pcompare(a,b); h = h[2]; } var o = $objfields(a); var i = 0; var l = $asize(o); while i < l { var v = $objget(b,o[i]); var k = @compare($objget(a,o[i]),v,$array(a,b,history)); if( k != 0 ) return k; i = i + 1; } return 0; } return $compare(a,b); } $exports.@compare = function(a,b) { @compare(a,b,null) }; $exports.@aget = function(a,i) { if( i < 0 || i >= a[2] ) $exports.invalid_arg('Array.get'); a[0][i] } $exports.@aset = function(a,i,v) { if( i < 0 || i >= a[2] ) $exports.invalid_arg('Array.set'); a[0][i] = v; } @empty = $array(0,function(_){ '[]' }); @pcons = function(c) { @string(c[2]) + ' :: ' + @string(c[3]) }; @cons = function(x,l) { $array(1,@pcons,x,l) } $exports.@pcons = @pcons; $exports.@cons = @cons; $exports.@empty = @empty; "); function magic(x) { neko("x"); } function throw(x : error) : 'a { neko("$throw(x)"); } function assert(file,line) { throw Assert_failure(file,line) } function invalid_arg(fun) : 'a { throw Invalid_argument(fun); } function error(x : string) { throw Error(x) } function ignore(x) { } function fst((a,_)) { a } function snd((_,a)) { a } function int(x:string) : int { var i = neko("$int(x)"); if i == neko("null") then invalid_arg(); i } function float(x:string) : float { var i = neko("$float(x)"); if i == neko("null") then invalid_arg(); i } function ord(x:char) : int { neko("x"); } function chr(x:int) : char { if x < 0 || x > 255 then invalid_arg(); neko("x"); } function string(x : 'a) : string { neko("@string(x)"); } function nstring(x : 'a) : string { neko("$string(x)"); } function print(x) : void { neko("$print(string(x))"); } function nprint(x) : void { neko("$print(x)"); } function compare(x : 'a, y : 'a) : int { neko("$exports.@compare(x,y)"); } function min(x,y) { if x < y then x else y } function max(x,y) { if x < y then y else x } function sprintf(fmt : 'a format, p : 'a ) : string { neko("@sprintf(fmt,p)") } function printf(fmt : 'a format, p : 'a ) : void { print (sprintf fmt p) } function stream(f : void -> 'a ) : 'a stream { neko("{ get => f, pos => 0, cache => $array() }"); } function stream_token( s : 'a stream, x : int ) : 'a { if x < 0 then invalid_arg(); neko(" var tmp = s.cache[x]; if( tmp != null ) return tmp; var n = $asize(s.cache); var c = $amake(x + 1); $ablit(c,0,s.cache,0,n); while n <= x { c[n] = s.get(null); n = n + 1; } s.cache = c; return c[x]; "); } function stream_junk( s : 'a stream, x : int ) : void { if x < 0 then invalid_arg(); neko(" var c = $asize(s.cache); s.pos = s.pos + x; if( c >= x ) s.cache = $asub(s.cache,x,c - x); else { s.cache = $array(); x = x - c; while x > 0 { s.get(null); x = x - 1; } } "); } function stream_pos( s : 'a stream ) : int { neko("s.pos"); } neko-2-4-0/src/core/Hashtbl.nml000066400000000000000000000035241464615675700163140ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type ('a,'b) t; function hash(x:'a) : int { neko("$hkey(x)") } function create() : ('a,'b) t { neko("$hnew(0)") } function length(h : ('a,'b) t) : int { neko("$hcount(h)") } function find(h : ('a,'b) t,k : 'a) : 'b { var v = neko("$hget(h,k,@Core.@compare)"); if neko("v == null") then throw Not_found; v } function exists(h : ('a,'b) t,k : 'a) : bool { neko("$hmem(h,k,@Core.@compare)"); } function add(h : ('a,'b) t, k : 'a, v : 'b) : void { neko("$hadd(h,k,v)"); } function remove(h : ('a,'b) t,k : 'a) : void { neko("$hremove(h,k,@Core.@compare)"); } function replace(h : ('a,'b) t,k : 'a,v : 'b) : void { neko("$hset(h,k,v,@Core.@compare)"); } function iter(f : 'a -> 'b -> void,h : ('a,'b) t) : void { neko("$hiter(h,f)"); } neko-2-4-0/src/core/IO.nml000066400000000000000000000174501464615675700152410ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type file type input { mutable in_read : void -> char; mutable in_input : string -> int -> int -> int; mutable in_close : void -> void; } type output { mutable out_write : char -> void; mutable out_output : string -> int -> int -> int; mutable out_close : void -> void; mutable out_flush : void -> void; } exception Overflow : string; exception Eof; exception Closed; exception Blocked; /* ---------------------------- RAW FILE API ------------------------------- */ neko(" @load = function(name,nargs) { return $loader.loadprim('std@'+name,nargs); } "); var file_open : string -> string -> file = neko("@load('file_open',2)"); var file_contents : string -> string = neko("@load('file_contents',1)"); var file_close : file -> void = neko("@load('file_close',1)"); var file_read : file -> string -> int -> int -> int = neko("@load('file_read',4)"); var file_read_char : file -> char = neko("@load('file_read_char',1)"); var file_write : file -> string -> int -> int -> int = neko("@load('file_write',4)"); var file_write_char : file -> char -> void = neko("@load('file_write_char',2)"); var file_flush : file -> void = neko("@load('file_flush',1)"); function file_input(f) { { in_read = function() { try file_read_char(f) catch { _ -> throw Eof } }; in_input = function(s,p,l) { try file_read f s p l catch { _ -> throw Eof } }; in_close = function() { file_close(f) }; } } function read_file(path,bin) { var f = file_open(path,if bin then "rb" else "r"); file_input f } function read_string(str) { var p = &0; var len = String.length str; { in_read = function() { if *p == len then throw Eof; var c = String.get str (*p); p := *p + 1; c }; in_input = function(s,pp,l) { if *p == len then throw Eof; var l = min l (len - *p); String.blit s pp str (*p) l; p := *p + l; l }; in_close = function() { } } } function file_output(f) { { out_write = file_write_char f; out_output = file_write f; out_close = function() { file_close(f) }; out_flush = function() { file_flush(f) }; } } function write_file(path,bin) { var f = file_open(path,if bin then "wb" else "w"); file_output f } function write_string() { var b = Buffer.create(); var o = { out_write = Buffer.add_char b; out_output = function(s,pp,l) { Buffer.add_sub b s pp l; l }; out_close = function() { }; out_flush = function() { }; }; (o , function() { Buffer.string b }) } var file_stdin : file = neko("@load('file_stdin',0)()"); var file_stdout : file = neko("@load('file_stdout',0)()"); var file_stderr : file = neko("@load('file_stderr',0)()"); var stdin : input = file_input file_stdin; var stdout : output = file_output file_stdout; var stderr : output = file_output file_stderr; /* ---------------------------- INPUT API ------------------------------- */ function create_in(read,input,close) { { in_read = read; in_input = input; in_close = close; } } function read_char(i) { i.in_read() } function read_byte(i) { ord i.in_read() } function input(i,s,p,n) { var len = String.length s; if p < 0 || n < 0 || p > len || p + n > len then invalid_arg(); i.in_input s p n } function read(i,n) { if n < 0 then invalid_arg(); if n == 0 then "" else { var s = String.create n; var p = &0; var len = &n; while *len > 0 { var n = i.in_input s (*p) (*len); if n == 0 then throw Blocked; p := *p + n; len := *len - n; }; s } } function read_buf(i,n) { if n < 0 then invalid_arg(); if n == 0 then "" else { var s = String.create n; var p = &0; var len = &n; try { while *len > 0 { var n = i.in_input s (*p) (*len); if n == 0 then throw (if *p == 0 then Blocked else Eof); p := *p + n; len := *len - n; }; s } catch { Eof -> if *p == 0 then throw Eof; String.sub s 0 (*p) } } } function read_all(i : input) : string { var b = Buffer.create(); var maxlen = 4096; try { while true { Buffer.add b (read_buf i maxlen); } magic(); } catch { Eof -> Buffer.string b } } function read_line(i : input) : string { var buf = Buffer.create(); try { while true { var ch = i.in_read(); if ch == '\n' then throw Exit; Buffer.add_char buf ch; } assert() } catch { Exit -> Buffer.string buf } } function read_i32(i) : int { var ch1 = read_byte i; var ch2 = read_byte i; var ch3 = read_byte i; var ch4 = read_byte i; ch1 or (ch2 << 8) or (ch3 << 16) or (ch4 << 24) } function read_ui16(i) { var ch1 = read_byte i; var ch2 = read_byte i; ch1 or (ch2 << 8) } function read_ui24(i) { var ch1 = read_byte i; var ch2 = read_byte i; var ch3 = read_byte i; ch1 or (ch2 << 8) or (ch3 << 16) } function read_i16(i) { var ch1 = read_byte i; var ch2 = read_byte i; var n = ch1 or (ch2 << 8); if ch2 and 128 != 0 then n - 65536 else n } function close_in(i) { i.in_close(); i.in_read := function() { throw Closed }; i.in_input := function(s,p,l) { throw Closed }; i.in_close := function() { throw Closed }; } /* ---------------------------- OUTPUT API ------------------------------- */ function create_out(write,output,flush,close) { { out_write = write; out_output = output; out_flush = flush; out_close = close; } } function write_char(o : output, x : char) : void { o.out_write x } function write_byte(o : output, x : int) : void { o.out_write (chr (x and 255)) } function write_i8(o : output, x : int) : void { if x < 127 || x > 128 then invalid_arg(); write_byte o (x and 0xFF) } function output(o,s,p,n) { var len = String.length s; if p < 0 || n < 0 || p > len || p + n > len then invalid_arg(); o.out_output s p n } function write(o : output,x : string) : void { var p = &0; var len = &(String.length x); while *len > 0 { var n = o.out_output x (*p) (*len); if n == 0 then throw Blocked; p := *p + n; len := *len - n; } } function write_i32(o : output, x : int) : void { write_byte o x; write_byte o (x >> 8); write_byte o (x >> 16); write_byte o (x >>> 24) } function write_ui16(o : output, x : int) : void { if x < 0 || x > 0xFFFF then invalid_arg(); write_byte o x; write_byte o (x >> 8) } function write_i16(o : output, x : int) : void { if x < -0x7FFF || x > 0x7FFF then invalid_arg(); if x < 0 then write_ui16 o (65536 + x) else write_ui16 o x } function write_ui24(o : output, x : int) : void { if x < 0 || x > 0xFFFFFF then invalid_arg(); write_byte o x; write_byte o (x >> 8); write_byte o (x >> 16); } function flush(o) { o.out_flush(); } function close_out(o) { o.out_close(); o.out_flush := function() { throw Closed }; o.out_write := function(_) { throw Closed }; o.out_output := function(_,_,_) { throw Closed }; o.out_close := function() { throw Closed }; } function printf(o : output, fmt : 'a format, p : 'a ) : void { write o (sprintf fmt p) } neko-2-4-0/src/core/LexEngine.nml000066400000000000000000000201731464615675700166040ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* most of this code is taken from the ULex lexer by Alain Frish, originaly written as an OCaml library. */ type charset = (int , int) list type node { mutable id : int; mutable trans : (charset , node) list; mutable epsilon : node list; } type state = node list type tables = (int array array , int array) type t { Empty; Match : charset; Star : t; Plus : t; Next : (t , t); Choice : (t , t); } /* ------- charset ------ */ var max_code = 255; var cempty = []; var call = [(0,max_code)]; function is_empty(c) { c == [] } function rec cunion(c1,c2) { match (c1 , c2) { | ([] , _) -> c2 | (_ , []) -> c1 | (((i1,j1) as s1)::r1, (i2,j2)::r2) -> if i1 <= i2 then if j1 + 1 < i2 then s1::(cunion r1 c2) else if (j1 < j2) then cunion r1 ((i1,j2)::r2) else cunion c1 r2 else cunion c2 c1 | _ -> assert() } } function ccomplement(c) { function rec loop(start,l) { match l { | [] -> if start <= max_code then [(start,max_code)] else [] | (i,j) :: l -> (start,i-1) :: (loop (j + 1) l) } }; match c { | (-1,j) :: l -> loop (j + 1) l | l -> loop (-1) l } } function cinter(c1,c2) { ccomplement (cunion (ccomplement c1) (ccomplement c2)) } function cdiff(c1,c2) { ccomplement (cunion (ccomplement c1) c2) } /* ------- nodes -------- */ function node(g) { { id = g(); trans = []; epsilon = []; } } function rec add_node(state,node) { if List.exists (function(k) { k === node }) state then state else add_nodes (node::state) node.epsilon } function rec add_nodes(state,nodes) { List.fold add_node state nodes } function nodes(nfa) { var g = &0; var gen = function() { g := *g + 1; *g }; function rec loop(n,final) { match n { | Empty -> final | Match c -> var n = node gen; n.trans := [(c,final)]; n | Star a -> var n = node gen; var an = loop a n; n.epsilon := [an; final]; n | Plus a -> var n = node gen; var an = loop a n; n.epsilon := [an; final]; an | Next (a,b) -> loop a (loop b final) | Choice (a,b) -> var n = node gen; n.epsilon := [loop a final; loop b final]; n } }; Array.map (function(n) { var f = node gen; (loop n f,f) }) nfa } /* ------- NFA -> DFA -------- */ function transitions(state : state) { // Merge transition with the same target function rec norm(l) { match l { | (t1,n1) :: (((t2,n2) :: q) as l) -> if n1 === n2 then norm ((cunion t1 t2,n1) :: q) else (t1,n1) :: (norm l) | _ -> l } }; var t = List.concat (List.map (function(n) { n.trans }) state); var t = norm (List.sort (function((t1,n1),(t2,n2)) { n1.id - n2.id }) t); // Split char sets so as to make them disjoint function rec split((all,t),(c0,n0)) { var t = (cdiff c0 all, [n0]) :: List.append (List.map (function((c,ns)) { (cinter c c0, n0::ns) }) t) (List.map (function((c,ns)) { (cdiff c c0, ns) }) t); (cunion all c0, List.filter (function((c,ns)) { !is_empty c }) t) }; var _ , t = List.fold split (cempty,[]) t; // Epsilon closure of targets var t = List.map (function((t,ns)) { (t,add_nodes [] ns) }) t; // Canonical ordering var t = List.array t; Array.sort (function((c1,ns1),(c2,ns2)) { compare c1 c2 }) t; (Array.map fst t, Array.map snd t) } function determinize(nfa : t array) { var nfa = nodes nfa; var h = Hashtbl.create(); var parts = Hashtbl.create(); var state_counter = &0; var part_counter = &0; var lparts = &[]; var states = &[]; function get_part(p) { try Hashtbl.find parts p catch { Not_found -> var pid = *part_counter; part_counter := *part_counter + 1; Hashtbl.add parts p pid; lparts := p :: *lparts; pid } }; function rec loop(state) { var sid = List.map (function(s) { s.id }) state; try Hashtbl.find h sid catch { Not_found -> var id = *state_counter; state_counter := *state_counter + 1; Hashtbl.add h sid id; var part, targets = transitions state; var part = get_part part; var targets = Array.map loop targets; var finals = Array.map (function((_,f)) { List.mem f state }) nfa; states := (id, (part,targets,finals)) :: *states; id } }; var init : node list ref = &[]; Array.iter (function((i,_)) { init := add_node (*init) i }) nfa; ignore(loop (*init)); var dfa = Array.init (*state_counter) (function(id) { List.assoc id (*states) }); function loop(part) { var seg = &[]; Array.iteri (function(i,c) { List.iter (function((a,b)) { seg := (a,b,i) :: *seg }) c }) part; List.sort (function((a1,_,_),(a2,_,_)) { compare a1 a2 }) (*seg); }; var trans = List.array (List.map loop (List.rev (*lparts))); (dfa , trans) } /* ---- DFA -> Tables ---- */ // this expands memory but is better since all accesses are O(1) function make_trans(trans,tbl) { var a = Array.make 256 (-1); List.iter (function((min,max,n)) { var i = &min; while *i <= max { a.[*i] := tbl.[n]; i := *i + 1; } }) trans; a } function make_tables((dfa,trans)) { function rec first(p,a) { if p == Array.length a then -1 else if a.[p] then p else first (p + 1) a }; var exits = Array.map (function((_,_,states)) { first 0 states }) dfa; var tbls = Array.map (function((tid,tbl,_)) { make_trans trans.[tid] tbl }) dfa; (tbls , exits) } /* ---- Regexp Parsing ---- */ exception InvalidRegexp : string function single(c) { var n = ord c; [(n,n)] } function group(chars) { var chars = List.map (function((c1,c2)) { [(ord c1,ord c2)] }) chars; List.fold cunion cempty chars } function invalid(s) { throw InvalidRegexp(s) } function escaped(s,i) { var c = String.get s (*i); i := *i + 1; match c { | '\\' | '+' | '*' | '?' | '[' | ']' | '-' -> c | _ -> invalid s } } function rec plus(r) { match r { | Next (r1,r2) -> Next(r1,plus r2) | _ -> Plus r } } function rec star(r) { match r { | Next (r1,r2) -> Next(r1,star r2) | _ -> Star r } } function rec opt(r) { match r { | Next(r1,r2) -> Next(r1,opt r2) | _ -> Choice(r,Empty) } } function next(r,r2) { match r { | Empty -> r2 | _ -> Next r r2 } } function parse( s : string ) : t { var i = &0; var r = &Empty; var l = String.length s; while *i < l { var c = String.get s (*i); i := *i + 1; match c { | '+' -> r := plus (*r) | '*' -> r := star (*r) | '?' -> r := opt (*r) | '[' -> var range = &None; function rec loop(acc) { var c = String.get s (*i); i := *i + 1; if c == ']' then { if *range != None then invalid s; acc } else if c == '-' then { if *range != None then invalid s; match acc { | [] -> loop ((c,c) :: acc) | (x1,x2) :: l -> if x1 != x2 then invalid s; range := Some x1; loop l } } else { var c = (if c == '\\' then escaped s i else c); match *range { | None -> loop ((c,c) :: acc) | Some c2 -> range := None; loop ((c2,c) :: acc) } } }; var c = String.get s (*i); var g = if c == '^' then { i := *i + 1; cdiff call (group (loop [])) } else group (loop []); r := next (*r) Match(g) | '\\' -> r := next (*r) Match(single (escaped s i)) | _ -> r := next (*r) Match(single c) } }; *r } neko-2-4-0/src/core/Lexer.nml000066400000000000000000000120771464615675700160110ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type pos { psource : string; pline : int; pmin : int; pmax : int; } type 'a t { mutable data : 'a; mutable current : string; mutable buffer : string; mutable bsize : int; mutable bin : int; mutable bpos : int; mutable cin : int; mutable cpos : int; mutable input : IO.input; mutable source : string; mutable line : int; mutable pos : int; mutable carriage : bool; } type ('a,'b) tables { engine : LexEngine.tables; cases : ('b t -> 'a) array; def : 'b t -> 'a; } exception Invalid_rule : string var null_pos = { pmin = 0; pmax = 0; pline = 0; psource = "" }; function source(p) { p.psource } function line(p) { p.pline } function punion(p,p2) { { psource = p.psource; pline = p.pline; pmin = min p.pmin p2.pmin; pmax = max p.pmax p2.pmax; } } function create(data) { var bufsize = 4096; { data = data; carriage = false; current = ""; buffer = String.create bufsize; bsize = bufsize; bin = 0; cin = 0; bpos = bufsize; cpos = bufsize; input = IO.read_string ""; source = ""; line = 0; pos = 0; } } function data(l) { l.data; } function set(l,data) { l.data := data; } function input(l,source,input,line,pos) { l.bin := 0; l.cin := 0; l.bpos := l.bsize; l.cpos := l.bsize; l.input := input; l.source := source; l.line := line; l.pos := pos; } function curpos(l) { { psource = l.source; pline = l.line; pmin = l.pos - String.length l.current; pmax = l.pos; } } function current(l) : string { l.current } function read(l) : char { if l.bin == 0 then { if l.bpos == l.bsize then { var buf = String.create (l.bsize * 2); String.blit buf l.bsize l.buffer 0 l.bsize; l.cpos := l.cpos + l.bsize; l.bpos := l.bpos + l.bsize; l.buffer := buf; l.bsize := l.bsize * 2; } var delta = l.bpos - l.cpos; String.blit l.buffer 0 l.buffer l.cpos delta; l.bpos := delta; l.cpos := 0; var k = IO.input l.input l.buffer delta (l.bsize - delta); l.bin := l.bin + k; l.cin := l.cin + k; }; var c = String.get l.buffer l.bpos; l.bpos := l.bpos + 1; l.bin := l.bin - 1; c } function inc_line(l,c) { if c == '\r' then l.carriage := true else if c == '\n' || l.carriage then { l.carriage := false; l.line := l.line + 1; }; } function char(l) : char option { try { var c = read l; l.bpos := l.bpos - 1; l.bin := l.bin + 1; inc_line l c; Some c } catch { IO.Eof -> None } } type cur { mutable cur_n : int; mutable cur_res : int; } function token(l,t) : 'a { var tbl = fst t.engine; var exits = snd t.engine; var cur = { cur_n = 0; cur_res = -1 }; var last = { cur_n = 0; cur_res = -1 }; function rec loop(n,k) { var res = neko("tbl[0][k][0][read(l)]"); var e = exits.[k]; cur.cur_n := n; cur.cur_res := res; if e != -1 then { last.cur_n := n; last.cur_res := e; } if res == -1 then throw Exit; neko("loop[0](n+1,res)"); }; function process(eof) { var n = last.cur_n; var k = last.cur_res; if k == -1 then { l.current := ""; if !eof then { l.bpos := l.bpos - (cur.cur_n + 1); l.bin := l.bin + (cur.cur_n + 1); } -1; } else { l.cin := l.cin - n; l.bin := l.cin; l.current := String.sub l.buffer l.cpos n; l.cpos := l.cpos + n; l.bpos := l.cpos; l.pos := l.pos + n; var i = &0; while *i < n { inc_line l (String.get l.current (*i)); i := *i + 1; } k } }; var k = try { loop 0 0; assert(); } catch { | IO.Eof -> if cur.cur_res != -1 then { last.cur_n := cur.cur_n + 1; last.cur_res := exits.[cur.cur_res]; } process true; | Exit -> process false } if k == -1 then t.def(l) else t.cases.[k](l) } function build(rules,def) { var nfa = List.map (function((r,_)) { try LexEngine.parse r catch { _ -> throw Invalid_rule(r) } }) rules; var cases = List.array (List.map snd rules); { engine = LexEngine.make_tables (LexEngine.determinize (List.array nfa)); cases = cases; def = def; } } function empty() { function empty_table(_) { invalid_arg() }; build [] empty_table; } neko-2-4-0/src/core/List.nml000066400000000000000000000100171464615675700156350ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ neko(" @make = function(s) { if( s == null ) return @Core.@empty; return @Core.@cons(s[0],@make(s[1])); } @rmake = function(s,x) { if( s == null ) return x; return @rmake(s[1],@Core.@cons(s[0],x)); } $exports.@rmake = function(s) { @rmake(s,@Core.@empty) }; $exports.@make = @make; "); function rec length(l) { match l { | [] -> 0 | _ :: l -> 1 + length l } } function hd(l) { match l { | [] -> invalid_arg() | x :: _ -> x } } function tl(l) { match l { | [] -> invalid_arg() | _ :: l -> l } } function rec map(f,l) { match l { | [] -> [] | x :: l -> f(x) :: map f l } } function rec iter(f,l) { match l { | [] -> () | x :: l -> f x; iter f l } } function rec iter2(f,l1,l2) { match l1 { | [] -> match l2 { | [] -> () | _ -> invalid_arg() } | x1 :: l1 -> match l2 { | [] -> invalid_arg() | x2 :: l2 -> f x1 x2; iter2 f l1 l2 } } } function rec split(l) { match l { | [] -> ([] , []) | (a,b) :: l -> var la , lb = split l; (a :: la, b :: lb) } } function rec exists(f,l) { match l { | [] -> false | x :: l -> if f x then true else exists f l } } function rec mem(v,l) { match l { | [] -> false | x :: l -> if x == v then true else mem v l } } function rec assoc(k,l) { match l { | [] -> throw Not_found | (k2,v) :: l -> if k == k2 then v else assoc k l } } function rec phys(k,l) { match l { | [] -> throw Not_found | (k2,v) :: l -> if k === k2 then v else phys k l } } function rec find(f,l) { match l { | [] -> throw Not_found | k :: l -> if f k then k else find f l } } function rec rev_rec(l,acc) { match l { | [] -> acc | x :: l -> rev_rec l (x :: acc) } } function rev(l) { rev_rec l [] } function rec fold(f,acc,l) { match l { | [] -> acc | x :: l -> fold f (f acc x) l } } function rec append(l1,l2) { match l1 { | [] -> l2 | x :: l1 -> x :: append l1 l2 } } function rec concat(ll) { function rec loop(ll,acc) { match ll { | [] -> acc | l :: ll -> loop ll (append acc l) } }; loop ll [] } function rec chop(n,l) { if n == 0 then l else match l { | [] -> invalid_arg() | _ :: l -> chop (n -1) l } } function rec all(f,l) { match l { | [] -> true | x :: l -> if f x then all(f,l) else false } } function rec none(f,l) { match l { | [] -> true | x :: l -> if f x then false else none(f,l) } } function nth(l,n) { function rec loop(l,n) { match l { | [] -> invalid_arg() | x :: l -> if n == 0 then x else loop l (n-1) } } if n < 0 then invalid_arg(); loop l n } function rec filter(f,l) { match l { | [] -> [] | x :: l -> if f x then x :: filter f l else filter f l } } function array(l) { match l { | [] -> Array.create() | x :: l -> var a = Array.make (1+ List.length l) x; function rec loop(p,l) { match l { | [] -> () | x :: l -> a.[p] := x; loop (p + 1) l } }; loop 1 l; a } } function sort(cmp,l) { var a = array l; Array.sort cmp a; Array.list a } neko-2-4-0/src/core/Map.nml000066400000000000000000000107071464615675700154450ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* This code is translated from OCaml ExtLib PMap Copyright (C) 1996-2003 Xavier Leroy, Nicolas Cannasse, Markus Mottl */ type ('k, 'v) map { Empty; Node : (('k, 'v) map , 'k , 'v , ('k, 'v) map , int); } type ('k, 'v) t { cmp : 'k -> 'k -> int; map : ('k, 'v) map; } function height(m) { match m { | Node (_, _, _, _, h) -> h | Empty -> 0 } } function make(l,k,v,r) { Node(l, k, v, r, max (height l) (height r) + 1) } function bal(l,k,v,r) { var hl = height l; var hr = height r; if hl > hr + 2 then match l { | Node (ll,lk,lv,lr,_) -> if height ll >= height lr then make ll lk lv (make lr k v r) else match lr { | Node (lrl,lrk,lrv,lrr,_) -> make (make ll lk lv lrl) lrk lrv (make lrr k v r) | Empty -> assert() } | Empty -> assert() } else if hr > hl + 2 then match r { | Node (rl,rk,rv,rr,_) -> if height rr >= height rl then make (make l k v rl) rk rv rr else match rl { | Node (rll, rlk, rlv, rlr, _) -> make (make l k v rll) rlk rlv (make rlr rk rv rr) | Empty -> assert() } | Empty -> assert() } else Node(l, k, v, r, max hl hr + 1) } function rec min_binding(m) { match m { | Node (Empty, k, v, _, _) -> (k, v) | Node (l, _, _, _, _) -> min_binding l | Empty -> throw Not_found } } function rec remove_min_binding(m) { match m { | Node (Empty, _, _, r, _) -> r | Node (l, k, v, r, _) -> bal (remove_min_binding l) k v r | Empty -> invalid_arg() } } function merge(t1,t2) { match (t1, t2) { | (Empty, _) -> t2 | (_, Empty) -> t1 | _ -> var k, v = min_binding t2; bal t1 k v (remove_min_binding t2) } } function create(cmp) { { cmp = cmp; map = Empty; } } function empty() { { cmp = compare; map = Empty; } } function is_empty(m) { m.map == Empty } function add(m,x,d) { var cmp = m.cmp; function rec loop(m) { match m { | Node (l, k, v, r, h) -> var c = cmp x k; if c == 0 then Node(l, x, d, r, h) else if c < 0 then { var nl = loop l; bal nl k v r; } else { var nr = loop r; bal l k v nr } | Empty -> Node(Empty, x, d, Empty, 1) } }; { cmp = cmp; map = loop m.map; } } function find(m,x) { var cmp = m.cmp; function rec loop(m) { match m { | Node (l, k, v, r, _) -> var c = cmp x k; if c < 0 then loop l else if c > 0 then loop r else v | Empty -> throw Not_found } }; loop m.map } function remove(m,x) { var cmp = m.cmp; function rec loop(m) { match m { | Node(l, k, v, r, _) -> var c = cmp x k; if c == 0 then merge l r else if c < 0 then bal (loop l) k v r else bal l k v (loop r) | Empty -> Empty } }; { cmp = cmp; map = loop m.map; } } function exists(m,x) { var cmp = m.cmp; function rec loop(m) { match m { | Node (l, k, v, r, _) -> var c = cmp x k; c == 0 || loop (if c < 0 then l else r) | Empty -> false } }; loop m.map } function iter(f,m) { function rec loop(m) { match m { | Empty -> () | Node (l, k, v, r, _) -> loop l; f k v; loop r } }; loop m.map } function map(f,m) { function rec loop(m) { match m { | Empty -> Empty | Node (l, k, v, r, h) -> var l = loop l; var r = loop r; Node(l, k, f v, r, h) } }; { cmp = m.cmp; map = loop m.map; } } function fold(f,m,acc) { function rec loop(acc,m) { match m { | Empty -> acc | Node (l, k, v, r, _) -> loop (f (loop acc l) v) r } }; loop acc m.map } neko-2-4-0/src/core/Math.nml000066400000000000000000000044251464615675700156210ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ function _load(name,nargs) : 'a { neko("return $loader.loadprim('std@math_'+name,nargs)"); } function _lload(name,nargs) : 'a { neko("return try $loader.loadprim('std@math_'+name,nargs) catch e function(_) $throw(e)"); } var atan2 : float -> float -> float = _load("atan2",2); var pow : float -> float -> float = _load("pow",2); var abs : float -> float = _load("abs",1); var iabs : int -> int = _load("abs",1); var ceil : float -> int = _load("ceil",1); var floor : float -> int = _load("floor",1); var round : float -> int = _load("round",1); var fceil : float -> float = _lload("fceil",1); var ffloor : float -> float = _lload("ffloor",1); var fround : float -> float = _lload("fround",1); var fint : float -> int = _lload("int",1); var pi : float = neko "$loader.loadprim('std@math_pi',0)()"; var sqrt : float -> float = _load("sqrt",1); var atan : float -> float = _load("atan",1); var cos : float -> float = _load("cos",1); var sin : float -> float = _load("sin",1); var tan : float -> float = _load("tan",1); var log : float -> float = _load("log",1); var exp : float -> float = _load("exp",1); var acos : float -> float = _load("acos",1); var asin : float -> float = _load("asin",1); neko-2-4-0/src/core/Net.nml000066400000000000000000000107561464615675700154620ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type socket; type ip = int32; // init neko "$loader.loadprim('std@socket_init',0)()"; var socket_new : bool -> socket = neko "$loader.loadprim('std@socket_new',1)"; var socket_close : socket -> void = neko "$loader.loadprim('std@socket_close',1)"; var socket_send_char : socket -> char -> void = neko "$loader.loadprim('std@socket_send_char',2)"; var socket_send : socket -> string -> int -> int -> int = neko "$loader.loadprim('std@socket_send',4)"; var socket_recv : socket -> string -> int -> int -> int = neko "$loader.loadprim('std@socket_recv',4)"; var socket_recv_char : socket -> char = neko "$loader.loadprim('std@socket_recv_char',1)"; var socket_write : socket -> string -> void = neko "$loader.loadprim('std@socket_write',2)"; var socket_read : socket -> string = neko "$loader.loadprim('std@socket_read',1)"; var socket_connect : socket -> ip -> int -> void = neko "$loader.loadprim('std@socket_connect',3)"; var socket_listen : socket -> int -> void = neko "$loader.loadprim('std@socket_listen',2)"; var socket_bind : socket -> ip -> int -> void = neko "$loader.loadprim('std@socket_bind',3)"; var socket_accept : socket -> socket = neko "$loader.loadprim('std@socket_accept',1)"; var socket_peer : socket -> (ip,int) = neko "$loader.loadprim('std@socket_peer',1)"; var socket_host : socket -> (ip,int) = neko "$loader.loadprim('std@socket_host',1)"; var socket_set_timeout : socket -> int -> void = neko "$loader.loadprim('std@socket_set_timeout',2)"; var host_resolve : string -> ip = neko "$loader.loadprim('std@host_resolve',1)"; var host_to_string : ip -> string = neko "$loader.loadprim('std@host_to_string',1)"; neko "@host_local = $loader.loadprim('std@host_local',0)"; function host_local() : string { neko "@host_local()" }; var url_encode : string -> string = neko "$loader.loadprim('std@url_encode',1)"; var url_decode : string -> string = neko "$loader.loadprim('std@url_decode',1)"; neko "@socket_select = $loader.loadprim('std@socket_select',4)" var _array_dep = Array.make; function socket_select( read : socket array, write : socket array, others : socket array, timeout : float option ) : (socket array, socket array, socket array) { var t = match timeout { None -> neko "null" | Some t -> t }; neko " var r = @socket_select($asub(read[0],0,read[2]),$asub(write[0],0,write[2]),$asub(others[0],0,others[2]),t); r[0] = @Array.@make(r[0]); r[1] = @Array.@make(r[1]); r[2] = @Array.@make(r[2]); r " } function socket_io(s) { (IO.create_in (function() { try socket_recv_char s catch { e -> throw IO.Eof } }) (function(b,p,l) { try { var n = socket_recv s b p l; if n == 0 then throw IO.Eof; n } catch { e -> throw IO.Eof } }) (function() { socket_close s }) ,IO.create_out (socket_send_char s) (socket_send s) (function() { }) (function() { socket_close s }) ) } function start_server( ip, port, on_connect, on_data ) { var sock = socket_new false; socket_bind sock ip port; socket_listen sock 10; var clients = &[]; var empty = Array.create(); while true { var r, _ , _ = socket_select List.array(sock :: List.map fst (*clients)) empty empty None; Array.iter (function(s) { if s == sock then { var s2 = socket_accept sock; clients := (s2,on_connect s2) :: *clients; } else { var cldata = List.assoc s (*clients); if !(on_data cldata) then clients := List.filter (function((s2,_)) { s != s2 }) (*clients); } }) r; } () } neko-2-4-0/src/core/Reflect.nml000066400000000000000000000077541464615675700163240ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type neko_object; type neko_abstract; type neko_function; type neko_array; type neko_loader; type module; type value { VNull; VInt : int; VFloat : float; VBool : bool; VString : string; VObject : neko_object; VAbstract : neko_abstract; VFunction : neko_function; VArray : neko_array; } neko(" @module_read = $loader.loadprim('std@module_read',2); @module_name = $loader.loadprim('std@module_name',1); @module_exports = $loader.loadprim('std@module_exports',1); @module_loader = $loader.loadprim('std@module_loader',1); @module_exec = $loader.loadprim('std@module_exec',1); @module_nglobals = $loader.loadprim('std@module_nglobals',1); @module_global_get = $loader.loadprim('std@module_global_get',2); @module_global_set = $loader.loadprim('std@module_global_set',3); @module_code_size = $loader.loadprim('std@module_code_size',1); @module_read_path = $loader.loadprim('std@module_read_path',3); "); function value( n : neko_value ) : value { var i = function() { invalid_arg() }; neko(" switch $typeof(n) { $tnull => VNull $tint => VInt(n) $tfloat => VFloat(n) $tbool => VBool(n) $tstring => VString(n) $tobject => VObject(n) $tabstract => VAbstract(n) $tfunction => VFunction(n) $tarray => VArray(n) default => i() } "); } function neko_value(v) : neko_value { match v { | VNull -> neko("null") | VInt i -> magic i | VFloat f -> magic f | VBool b -> magic b | VString s -> magic s | VObject o -> magic o | VAbstract a -> magic a | VFunction f -> magic f | VArray a -> magic a } } function asize(a : neko_array) : int { neko("$asize(a)"); } function aget(a : neko_array, p : int) : value { value(neko("a[p]")); } function aset(a : neko_array, p : int, v : value) : void { var v = neko_value v; neko("a[p] = v"); } function module_read( fread : string -> int -> int -> int ) : module { neko("@module_read(fread,$loader)"); } var __list_dep = List.map function loader_path() : string list { neko("@List.@make($loader.path)"); } function module_read_path( path : string array, name : string, loader : neko_loader ) : module { neko(" var h; var n = path[2]; path = path[0]; while( n > 0 ) { n -= 1; h = $array(path[n],h); } @module_read_path(h,name,loader); "); } function module_name( m : module ) : string { neko("@module_name(m)"); } function module_execute( m : module ) { value neko("@module_exec(m)"); } function module_exports( m : module ) { value neko("@module_exports(m)") } function module_loader( m : module ) { value neko("@module_loader(m)") } function module_globals_count( m : module ) : int { neko("@module_nglobals(m)"); } function module_get_global( m : module, n : int ) { value neko("@module_global_get(m,n)"); } function module_set_global( m : module, n : int, v : value ) : void { var v = neko_value v; neko("@module_global_set(m,n,v)"); } function module_code_size( m : module ) : int { neko("@module_code_size(m)"); } neko-2-4-0/src/core/Regexp.nml000066400000000000000000000035601464615675700161610ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type t neko(" regexp_matched_pos = $loader.loadprim('regexp@regexp_matched_pos',2); "); var build : string -> t = neko("$loader.loadprim('regexp@regexp_new',1)"); var find : t -> string -> int -> int -> bool = neko("$loader.loadprim('regexp@regexp_match',4)"); var matched : t -> int -> string = neko("$loader.loadprim('regexp@regexp_matched',2)"); function matched_pos( r : t, n : int ) : (int , int) { neko(" var s = regexp_matched_pos(r,n); $array(s.pos,s.len); "); } function split( r, str ) { function rec loop(pos,len) { if !find r str pos len then [String.sub str pos len] else { var ppos, plen = matched_pos r 0; var tot = ppos - pos + plen; String.sub str pos (ppos - pos) :: loop (pos + tot) (len - tot) } } loop 0 String.length(str) }neko-2-4-0/src/core/Set.nml000066400000000000000000000136141464615675700154630ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* This is very similar to PMap except that it does only store one value */ type 'a set { Empty; Node : ('a set, 'a ,'a set, int); } type 'a t { cmp : 'a -> 'a -> int; set : 'a set; } /* -------- tools ----------- */ function height(s) { match s { | Empty -> 0 | Node(_, _, _, h) -> h } } function make(l,v,r) { Node(l, v, r, max (height l) (height r) + 1) } function bal(l,v,r) { var hl = height l; var hr = height r; if hl > hr + 2 then match l { | Node (ll,lv,lr,_) -> if height ll >= height lr then make ll lv (make lr v r) else match lr { | Node (lrl,lrv,lrr,_) -> make (make ll lv lrl) lrv (make lrr v r) | Empty -> assert() } | Empty -> assert() } else if hr > hl + 2 then match r { | Node (rl,rv,rr,_) -> if height rr >= height rl then make (make l v rl) rv rr else match rl { | Node (rll, rlv, rlr, _) -> make (make l v rll) rlv (make rlr rv rr) | Empty -> assert() } | Empty -> assert() } else Node l v r (max hl hr + 1) } function rec remove_min_elt(s) { match s { | Empty -> assert() | Node(Empty, v, r, _) -> r | Node(l, v, r, _) -> bal (remove_min_elt l) v r } } function rec min_elt(s) { match s { | Empty -> throw Not_found | Node(Empty, v, r, _) -> v | Node(l, v, r, _) -> min_elt l } } function merge(t1,t2) { match (t1, t2) { | (Empty, t) -> t | (t, Empty) -> t | _ -> bal t1 (min_elt t2) (remove_min_elt t2) } } function rec add_loop(cmp,x,s) { match s { | Empty -> Node(Empty,x,Empty,1) | Node(l, v, r, _) -> var c = cmp x v; if c == 0 then s else if c < 0 then bal (add_loop cmp x l) v r else bal l v (add_loop cmp x r) } } function rec join(cmp,l,v,r) { match (l, r) { | (Empty, _) -> add_loop cmp v r | (_, Empty) -> add_loop cmp v l | (Node(ll, lv, lr, lh), Node(rl, rv, rr, rh)) -> if lh > rh + 2 then bal ll lv (join cmp lr v r) else if rh > lh + 2 then bal (join cmp l v rl) rv rr else make l v r | _ -> assert() } } function concat(cmp,t1,t2) { match (t1, t2) { | (Empty, t) -> t | (t, Empty) -> t | _ -> join cmp t1 (min_elt t2) (remove_min_elt t2) } } function rec split(cmp,x,s) { match s { | Empty -> (Empty, false, Empty) | Node(l, v, r, _) -> var c = cmp x v; if c == 0 then (l, true, r) else if c < 0 then { var ll, pres, rl = split cmp x l; (ll, pres, join cmp rl v r) } else { var lr, pres, rr = split cmp x r; (join cmp l v lr, pres, rr) } } } /* ---------- api ----------- */ function rec add(set : 'a t, x : 'a) { { cmp = set.cmp; set = add_loop set.cmp x set.set; } } function create(cmp) { { cmp = cmp; set = Empty; } } function empty() { { cmp = compare; set = Empty; } } function is_empty(s) { match s.set { | Empty -> true | _ -> false } } function exists(set,x) { function rec loop(s) { match s { | Empty -> false | Node(l, v, r, _) -> var c = set.cmp x v; c == 0 || loop (if c < 0 then l else r) } } loop set.set } function remove(set,x) { function rec loop(s) { match s { | Empty -> Empty | Node(l, v, r, _) -> var c = set.cmp x v; if c == 0 then merge l r else if c < 0 then bal (loop l) v r else bal l v (loop r) } } { cmp = set.cmp; set = loop set.set; } } function union(set1,set2) { var cmp = set1.cmp; function rec loop(s1,s2) { match (s1, s2) { | (Empty, t2) -> t2 | (t1, Empty) -> t1 | (Node(l1, v1, r1, h1), Node(l2, v2, r2, h2)) -> if h1 >= h2 then if h2 == 1 then add_loop cmp v2 s1 else { var l2, _, r2 = split cmp v1 s2; join cmp (loop l1 l2) v1 (loop r1 r2) } else if h1 == 1 then add_loop cmp v1 s2 else { var l1, _, r1 = split cmp v2 s1; join cmp (loop l1 l2) v2 (loop r1 r2) } | _ -> assert() } } { cmp = cmp; set = loop set1.set set2.set; } } function inter(set1,set2) { var cmp = set1.cmp; function rec loop(s1,s2) { match (s1, s2) { | (Empty, t2) -> Empty | (t1, Empty) -> Empty | (Node(l1, v1, r1, _), t2) -> match split cmp v1 t2 { | (l2, false, r2) -> concat cmp (loop l1 l2) (loop r1 r2) | (l2, true, r2) -> join cmp (loop l1 l2) v1 (loop r1 r2) | _ -> assert() } | _ -> assert() } } { cmp = cmp; set = loop set1.set set2.set; } } function diff(set1,set2) { var cmp = set1.cmp; function rec loop(s1,s2) { match (s1, s2) { | (Empty, t2) -> Empty | (t1, Empty) -> t1 | (Node(l1, v1, r1, _), t2) -> match split cmp v1 t2 { | (l2, false, r2) -> join cmp (loop l1 l2) v1 (loop r1 r2) | (l2, true, r2) -> concat cmp (loop l1 l2) (loop r1 r2) | _ -> assert() } | _ -> assert() } } { cmp = cmp; set = loop set1.set set2.set; } } function iter(f,set) { function rec loop(s) { match s { | Empty -> () | Node(l, v, r, _) -> loop l; f v; loop r } } loop set.set } neko-2-4-0/src/core/Stack.nml000066400000000000000000000036761464615675700160040ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type stack_item { CFunction; Module : string; Pos : (string,int); } type stack = stack_item array; neko(" @make_stack = function(a) { var a = $acopy(a); var i = 0; var l = $asize(a); while( i < l ) { var k = a[i]; a[i] = if( k == null ) CFunction else if( $typeof(k) == $tstring ) Module(k) else Pos(k[0],k[1]); i = i + 1; } return @Array.@make(a); } ") function call() : stack { neko("@make_stack($callstack())"); } function exc() : stack { neko("@make_stack($excstack())"); } function dump(ch,stack) { Array.iter (function(s) { match s { | CFunction -> IO.write ch "Called from a C function\n" | Module m -> IO.printf ch "Called from %s (no debug available)\n" m | Pos (file,line) -> IO.printf ch "Called from %s line %d\n" (file,line) } }) stack } function print() { dump(IO.stdout,call()); } neko-2-4-0/src/core/String.nml000066400000000000000000000116701464615675700161760ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ neko(" @string_split = $loader.loadprim('std@string_split',2); "); function make(size : int, c : char) : string { neko(" var s = $smake(size); var i = 0; while( i < size ) { $sset(s,i,c); i = i + 1; } s "); } var create : int -> string = neko("$smake"); var length : string -> int = neko("$ssize"); function get(s : string,n : int) : char { var c = neko("$sget(s,n)"); if c == neko("null") then invalid_arg(); c } function set(s : string, n : int, c : char) : void { if neko("$sset(s,n,c) == null") then invalid_arg(); } function blit(dst : string, p : int, src : string, p2 : int, l : int ) : void { if neko("try { $sblit(dst,p,src,p2,l); false; } catch e true") then invalid_arg(); } function sub(s : string, p : int, l : int ) : string { var s = neko("$ssub(s,p,l)"); if s == neko("null") then invalid_arg(); s } function find( s : string, p : int, pat : string ) : int { var pos = neko "$sfind(s,p,pat)"; if pos == neko "null" then throw Not_found; pos } var list_depency = List.iter function split( s : string, sub : string ) : string list { neko(" var l = @string_split(s,sub); return @List.@make(l); "); } function concat( sep : string, s : string list ) : string { var b = Buffer.create(); function rec loop(l) { match l { | [] -> () | [x] -> Buffer.add b x | x :: l -> Buffer.add b x; Buffer.add b sep; loop l } }; loop(s); Buffer.string b } function is_printable(c) { c >= '\032' && c <= '\126' } function escape_char(c) { match c { | '\n' -> "\\n" | '\t' -> "\\t" | '\r' -> "\\r" | '\\' -> "\\\\" | '"' -> "\\\"" | c when !is_printable c -> sprintf "\\%.3d" (ord c) | c -> String.make 1 c } } function escape(s) { var b = Buffer.create(); function rec loop(i) { if i == String.length s then Buffer.string b else { match String.get s i { | '\n' -> Buffer.add b "\\n" | '\t' -> Buffer.add b "\\t" | '\r' -> Buffer.add b "\\r" | '\\' -> Buffer.add b "\\\\" | '"' -> Buffer.add b "\\\"" | c when !is_printable c -> Buffer.add b (sprintf "\\%.3d" (ord c)) | c -> Buffer.add_char b c }; loop (i+1) } }; loop(0); } function unescape(s) { var l = length s; var s2 = create l; var i = &0; var p = &0; while *i < l { match neko("$sget")(s,*i) { | '\\' -> i := *i + 1; if *i == l then invalid_arg(); var c = neko("$sget")(s,*i); match c { | 'n' -> neko("$sset")(s2,*p,'\n'); | 't' -> neko("$sset")(s2,*p,'\t'); | 'r' -> neko("$sset")(s2,*p,'\r'); | '"' -> neko("$sset")(s2,*p,'"'); | '\\' -> neko("$sset")(s2,*p,'\\'); | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' -> if *i + 2 >= l then invalid_arg(); var c2 = neko("$sget")(s,*i + 1); var c3 = neko("$sget")(s,*i + 2); i := *i + 2; if c2 < '0' || c2 > '9' || c3 < '0' || c3 > '9' then invalid_arg(); var o0 = ord '0'; var n = (ord c - o0) * 100 + (ord c2 - o0) * 10 + (ord c3 - o0); if n > 255 then invalid_arg(); neko("$sset")(s2,*p,chr n); | _ -> invalid_arg() } | c -> neko("$sset")(s2,*p,c); } p := *p + 1; i := *i + 1; } sub s2 0 (*p) } function lowercase(s) { var l = length s; var s2 = sub s 0 l; var i = &0; var delta = ord 'a' - ord 'A'; while *i < l { var c = neko("$sget")(s,*i); if c >= 'A' && c <= 'Z' then neko("$sset")(s2,*i,chr(ord c + delta)); i := *i + 1; } s2 } function uppercase(s) { var l = length s; var s2 = sub s 0 l; var i = &0; var delta = ord 'A' - ord 'a'; while *i < l { var c = neko("$sget")(s,*i); if c >= 'a' && c <= 'z' then neko("$sset")(s2,*i,chr(ord c + delta)); i := *i + 1; } s2 } neko(" @serialize = $loader.loadprim('std@serialize',1); @unserialize = $loader.loadprim('std@unserialize',2); "); function serialize( x : 'a ) : string { neko "@serialize(x)"; } function unserialize( x : string ) : 'a { neko "@unserialize(x,$loader)"; } neko-2-4-0/src/core/Sys.nml000066400000000000000000000054571464615675700155140ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ neko(" @sys_exit = $loader.loadprim('std@sys_exit',1); @get_env = $loader.loadprim('std@get_env',1); @get_cwd = $loader.loadprim('std@get_cwd',0); @exe_path = $loader.loadprim('std@sys_exe_path',0); @sys_file_type = $loader.loadprim('std@sys_file_type',1); @sys_read_dir = $loader.loadprim('std@sys_read_dir',1); "); type version { maj : int; min : int; build : int; } function without_extension(s) { match List.rev (String.split s ".") { | [] -> "" | [x] -> x | ext :: l -> String.concat "." (List.rev l) } } function extension(s) { match List.rev (String.split s ".") { | [] | [_] -> "" | ext :: _ -> ext } } function without_dir(s) { var s = String.concat "/" (String.split s "\\"); match List.rev (String.split s "/") { | [] -> assert() | file :: _ -> file } } var array_dependency = Array.make function args() : string array { neko(" @Array.@make($loader.args) "); } function exit(code : int) : 'a { neko(" @sys_exit(code); "); } var exists : string -> bool = neko "$loader.loadprim('std@sys_exists',1)" var version = { var v : int = neko "$version()"; { maj = v / 100; min = (v / 10) % 10; build = v % 10; } } function get_env( s : string ) : string option { var s = neko "@get_env(s)"; if s == neko "null" then None else Some s; } var put_env : string -> string -> void = neko "$loader.loadprim('std@put_env',2)" function get_cwd() : string { neko "@get_cwd()" } var set_cwd : string -> void = neko "$loader.loadprim('std@set_cwd',1)" function executable_path() : string { neko "@exe_path()" } function is_directory(str:string) : bool { neko "@sys_file_type(str) == 'dir'"; } function read_directory(str:string) : string list { neko "@List.@make(@sys_read_dir(str))" } neko-2-4-0/src/core/Xml.nml000066400000000000000000000074311464615675700154700ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type t { Node : (string, (string, string) list , t list); PCData : string; CData : string; Document : t list; } exception Error : string; var __list = List.hd; neko(" @parser = function() { return { stack => null, doc => null, cdata => function(s) { this.doc = $array(CData(s),this.doc); }, pcdata => function(s) { this.doc = $array(PCData(s),this.doc); }, xml => function(name,att) { var fields = $objfields(att); var nf = $asize(fields); var fl = null; var i = 0; while( i < nf ) { var f = fields[i]; fl = $array($array($field(f),$objget(att,f)),fl); i = i + 1; } this.stack = $array(name,fl,this.doc,this.stack); this.doc = null; }, done => function() { var s = this.stack; var d = this.doc; this.doc = $array(Node(s[0],@List.@rmake(s[1]),@List.@rmake(d)),s[2]); this.stack = s[3]; } } } @parse = $loader.loadprim('std@parse_xml',2); "); function parse( s : string ) : t { neko(" var p = @parser(); try { @parse(s,p); return Document(@List.@rmake(p.doc)); } catch e { if( $typeof(e) == $tstring ) $rethrow(Error(e)); $rethrow(e); } "); } function is_node(x) { match x { | Node _ -> true | _ -> false } } function is_pcdata(x) { match x { | PCData _ -> true | _ -> false } } function is_cdata(x) { match x { | CData _ -> true | _ -> false } } function firstNode(x) { match x { | Node (_,_,l) | Document l -> List.find is_node l | CData _ | PCData _ -> invalid_arg() } } function nodes(x) { match x { | Node (_,_,l) | Document l -> List.filter is_node l | CData _ | PCData _ -> invalid_arg() } } function node_name(x) { match x { | Node (name,_,_) -> name | _ -> invalid_arg() } } function node_text(x) { match x { | Node (_,_,children) -> var b = Buffer.create(); List.iter (function(x) { match x { | CData t | PCData t -> Buffer.add b t | _ -> invalid_arg() } }) children; Buffer.string b | _ -> invalid_arg() } } function attrib(x,n) { var n = String.lowercase n; match x { | Node (_,att,_) -> snd (List.find (function((n2,_)) { String.lowercase n2 == n }) att) | _ -> invalid_arg() } } function rec write(ch,x) { match x { | Document l -> List.iter write(ch) l | CData c -> IO.write ch ""; | PCData c -> IO.write ch c; | Node (name,att,children) -> IO.printf ch "<%s%s" (name,String.concat "" (List.map (function((a,v)) { " "+a+"=\""+String.escape v+"\"" }) att)); match children { | [] -> IO.write ch "/>" | l -> IO.write ch ">"; List.iter write(ch) l; IO.printf ch "" name } } } function to_string(x) { var ch , str = IO.write_string(); write ch x; str() } neko-2-4-0/src/core/Zip.nml000066400000000000000000000064231464615675700154720ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type in; type out; type flush { NO; SYNC; FULL; FINISH; BLOCK; } type result { done : bool; read : int; write : int; } function init() { neko(" @set_flush_mode = $loader.loadprim('zlib@set_flush_mode',2); @inflate_init = $loader.loadprim('zlib@inflate_init',1); @deflate_buffer = $loader.loadprim('zlib@deflate_buffer',5); @inflate_buffer = $loader.loadprim('zlib@inflate_buffer',5); @deflate_init = $loader.loadprim('zlib@deflate_init',1); @deflate_bound = $loader.loadprim('zlib@deflate_bound',2); @deflate_end = $loader.loadprim('zlib@deflate_end',1); @inflate_end = $loader.loadprim('zlib@inflate_end',1); "); } function __result(x) { { done = neko "x.done"; read = neko "x.read"; write = neko "x.write" } } function output(v:int) : out { neko("@deflate_init")(v); } function output_bound(o:out,size:int) : int { neko("@deflate_bound")(o,size); } function output_end(o:out) : void { neko("@deflate_end")(o); } function output_buffer(o:out,in:string,ipos:int,out:string,opos:int) { __result(neko "@deflate_buffer(o,in,ipos,out,opos)"); } function input( n : int option ) : in { neko("@inflate_init")(match n { None -> neko "null" | Some i -> i }) } function input_buffer(i:in,in:string,ipos:int,out:string,opos:int) { __result(neko "@inflate_buffer(i,in,ipos,out,opos)"); } function input_end(i:in) : void { neko("@inflate_end")(i); } function output_set_flush_mode(o:out,f:flush) : void { neko("@set_flush_mode")(o,string f); } function input_set_flush_mode(i:in,f:flush) : void { neko("@set_flush_mode")(i,string f); } function compress(str,level) { var c = output level; output_set_flush_mode c FINISH; var out = String.create (output_bound c (String.length str)); var r = output_buffer c str 0 out 0; output_end c; if !r.done || r.read != String.length str then throw Error("Compression failed"); String.sub out 0 r.write; } function uncompress(str) { var u = input None; var tmp = String.create (1 << 16); // 64K var b = Buffer.create(); input_set_flush_mode u SYNC; function rec loop(pos) { var r = input_buffer u str pos tmp 0; Buffer.add_sub b tmp 0 r.write; if !r.done then loop(pos + r.read); } loop(0); input_end u; Buffer.string b; } neko-2-4-0/src/mtypes/000077500000000000000000000000001464615675700146045ustar00rootroot00000000000000neko-2-4-0/src/mtypes/std.neko000066400000000000000000000342061464615675700162610ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ version = 0; // ---------------------------------------------------------------------- // List List = $new(null); List.new = function() { var o = $new(null); $objsetproto(o,@List); o.l = 0; o.h = null; o.q = null; return o; }; @List = $new(null); List.prototype = @List; @List.get_length = function() { return this.l; }; @List.add = function(x) { var a = $array(x,null); if( this.q != null ) this.q[1] = a; else this.h = a; this.q = a; this.l = this.l + 1; }; @List.push = function(x) { var a = $array(x,this.h); this.h = a; if( this.q == null ) this.q = a; this.l = this.l + 1; }; @List.remove = function(x) { var cur = this.h; var prev = null; while( cur != null ) { if( cur[0] == x ) { if( prev == null ) this.h = cur[1]; else prev[1] = cur[1]; if( this.q == cur ) this.q = prev; this.l = this.l - 1; return true; } prev = cur; cur = cur[1]; } return false; } @List.at = function(p) { var cur = this.h; while( cur != null ) { if( $not(p) ) return cur[0]; p = p - 1; cur = cur[1]; } return null; } @List.pop = function() { if( this.h == null ) return null; if( this.q == this.h ) this.q = null; var x = this.h[0]; this.h = this.h[1]; this.l = this.l - 1; return x; } @List.first = function() { if( this.h == null ) return null; return this.h[0]; } @List.last = function() { if( this.q == null ) return null; return this.q[0]; } @List.iter = function(f) { var p = this.h; while( p != null ) { f(p[0]); p = p[1]; } } @List.find = function(f) { var p = this.h; while( p != null ) { var x = f(p[0]); if( x != null ) return x; p = p[1]; } return null; } @List.exists = function(f) { var p = this.h; while( p != null ) { if( f(p[0]) ) return true; p = p[1]; } return false; } @List.join = function(sep) { if( $not(this.l) ) return String.new(""); var b = StringBuf.new(); var p = this.h; while( p != null ) { b.add(p[0]); p = p[1]; if( p != null ) b.add(sep); } return String.new(b.__string()); } @List.toArray = function() { var a = $amake(this.l); var p = this.h; var n = 0; while( p != null ) { a[n] = p[0]; n = n + 1; p = p[1]; } return Array.new1(a); } @List.toString = function() { return String.new(this.__string()); } @List.__string = function() { var b = StringBuf.new(); var p = this.h; b.add("["); while( p != null ) { b.add(p[0]); p = p[1]; if( p != null ) b.add(","); } b.add("]"); return b.__string(); } @List.map = function(f) { var l = List.new(); if( this.h != null ) { var cur = this.h; var last = null; l.h = null; while( cur != null ) { if( last == null ) { last = $array(f(cur[0]),null); l.h = last; } else { var tmp = $array(f(cur[0]),null); last[1] = tmp; last = tmp; } cur = cur[1]; } l.q = last; l.l = this.l; } return l; } @List.filter = function(f) { var l = List.new(); if( this.h != null ) { var cur = this.h; var last = null; var count = 0; l.h = null; while( cur != null ) { if( f(cur[0]) ) { if( last == null ) { last = $array(cur[0],null); l.h = last; } else { var tmp = $array(cur[0],null); last[1] = tmp; last = tmp; } count += 1; } cur = cur[1]; } l.q = last; l.l = count; } return l; } @List.__serialize = function() { return $array("List",version); } $exports.List = List; // ---------------------------------------------------------------------- // Array Array = $new(null); Array.new = function() { var o = $new(null); $objsetproto(o,@Array); o.@a = $array(); return o; } Array.new1 = function(a) { var o = $new(null); $objsetproto(o,@Array); o.@a = a; return o; } @Array = $new(null); Array.prototype = @Array; @Array.get_length = function() { return $asize(this.@a); } @Array.concat = function(a) { var l = $asize(this.@a); var l2 = $asize(a.@a); var a = $amake(l+l2); $ablit(a,0,this.@a,0,l); $ablit(a,l,a.@a,0,l2); this.@a = a; } @Array.sub = function(s,l) { var a = $asub(this.@a,s,l); if( a == null ) return null; return Array.new1(a); } @Array.sort = function(f) { var a = this.@a; var i = 0; var l = $asize(a); while( i < l ) { var swap = false; var j = 0; var max = l - i - 1; while( j < max ) { if( f(a[j],a[j+1]) > 0 ) { var tmp = a[j+1]; a[j+1] = a[j]; a[j] = tmp; swap = true; } j = j + 1; } if( $not(swap) ) break; i = i + 1; } } @Array.iter = function(f) { var i = 0; var a = this.@a; var l = $asize(a); while( i < l ) { f(a[i]); i = i + 1; } } @Array.map = function(f) { var i = 0; var a = this.@a; var l = $asize(a); var a2 = $amake(l); while( i < l ) { a2[i] = f(a[i]); i = i + 1; } return Array.new1(a2); } @Array.join = function(sep) { var i = 0; var a = this.@a; var l = $asize(a); var b = StringBuf.new(); while( i < l ) { b.add(a[i]); i = i + 1; if( i != l ) b.add(sep); } return b.toString(); } @Array.toList = function() { var i = 0; var a = this.@a; var l = $asize(a); var li = List.new(); while( i < l ) { li.add(a[i]); i = i + 1; } return li; } @Array.toString = function() { return String.new(this.__string()); } @Array.__string = function() { return $string(this.@a); } @Array.set = function(p,v) { var a = this.@a; if( $asize(a) > p ) return (a[p] = v); var a2 = $amake(p+1); $ablit(a2,0,a,0,$asize(a)); this.@a = a2; return (a2[p] = v); } @Array.__serialize = function() { return $array("Array",version); } $exports.Array = Array; // ---------------------------------------------------------------------- // Hash Hash = $new(null); Hash.new = function() { var o = $new(null); $objsetproto(o,@Hash); o.@h = $hnew(3); return o; } @Hash = $new(null); Hash.prototype = @Hash; @Hash.get = function(k) { return $hget(this.@h,k.@s,null); } @Hash.set = function(k,v) { $hset(this.@h,k.@s,v,null); } @Hash.remove = function(k) { return $hremove(this.@h,k.@s,null); } @Hash.exists = function(k) { return $hmem(this.@h,k.@s,null); } @Hash.iter = function(f) { $hiter(this.@h,function(k,v) { f(String.new(k),v) }); } @Hash.toString = function() { return String.new(this.__string()); } @Hash.__string = function() { var s = StringBuf.new(); var i = 0; var first = true; s.add("#hash["); $hiter(this.@h,function(k,v) { if( first ) first = false; else s.add(", "); s.add(k); s.add(" => "); s.add(v); }); s.add("]"); return s.__string(); } @Hash.__serialize = function() { return $array("Hash",version); } $exports.Hash = Hash; // ---------------------------------------------------------------------- // String string_split = $loader.loadprim("std@string_split",2); String = $new(null); String.new = function(s) { if( s == null ) return null; if( $typeof(s) != $tstring ) s = $string(s); var o = $new(null); $objsetproto(o,@String); o.@s = s; return o; } @String = $new(null); String.prototype = @String; @String.get_length = function() { return $ssize(this.@s); } @String.charAt = function(p) { return String.new( $ssub(this.@s,p,1) ); } @String.at = function(p) { return $sget(this.@s,p); } @String.sub = function(s,l) { if( l == null ) l = $ssize(this.@s) - s; return String.new( $ssub(this.@s,s,l) ); } @String.split = function(s) { var a = string_split(this.@s,s.@s); var l = List.new(); while( a != null ) { l.add( String.new(a[0]) ); a = a[1]; } return l; } @String.toString = function() { return this; } @String.__string = function() { return this.@s; } @String.__compare = function(o) { return $compare(this.@s,o.@s); } @String.__add = function(s) { return String.new(this.@s+$string(s)); } @String.__radd = function(s) { return String.new($string(s)+this.@s); } @String.__serialize = function() { return $array("String",version); } $exports.String = String; // ---------------------------------------------------------------------- // Int32 Int32 = $new(null); Int32.of_int = $loader.loadprim("std@int32_new",1); Int32.to_int = $loader.loadprim("std@int32_to_int",1); Int32.add = $loader.loadprim("std@int32_add",2); Int32.sub = $loader.loadprim("std@int32_sub",2); Int32.mul = $loader.loadprim("std@int32_mul",2); Int32.div = $loader.loadprim("std@int32_div",2); Int32.mod = $loader.loadprim("std@int32_mod",2); Int32.shl = $loader.loadprim("std@int32_shl",2); Int32.shr = $loader.loadprim("std@int32_shr",2); Int32.ushr = $loader.loadprim("std@int32_ushr",2); Int32.and = $loader.loadprim("std@int32_and",2); Int32.xor = $loader.loadprim("std@int32_xor",2); Int32.or = $loader.loadprim("std@int32_or",2); Int32.neg = $loader.loadprim("std@int32_neg",1); Int32.complement = $loader.loadprim("std@int32_complement",1); Int32.compare = $loader.loadprim("std@int32_compare",2); $exports.Int32 = Int32; // ---------------------------------------------------------------------- // StringBuf buffer_new = $loader.loadprim("std@buffer_new",0); buffer_add = $loader.loadprim("std@buffer_add",2); buffer_add_sub = $loader.loadprim("std@buffer_add_sub",4); buffer_add_char = $loader.loadprim("std@buffer_add_char",2); buffer_string = $loader.loadprim("std@buffer_string",1); StringBuf = $new(null); StringBuf.new = function() { var b = $new(null); $objsetproto(b,@StringBuf); b.@b = buffer_new(); return b; } @StringBuf = $new(null); StringBuf.prototype = @StringBuf; @StringBuf.add = function(s) { buffer_add(this.@b,s); } @StringBuf.addSub = function(s,p,l) { buffer_add_sub(this.@b,s,p,l); } @StringBuf.addChar = function(c) { buffer_add_char(this.@b,c); } @StringBuf.__string = function() { return buffer_string(this.@b); } @StringBuf.toString = function() { return String.new(this.__string()); } $exports.StringBuf = StringBuf; // ---------------------------------------------------------------------- // Xml parse_xml = $loader.loadprim("std@parse_xml",2); fxml = function(name,att) { var o = $new(null); $objsetproto(o,@Xml); var f = $objfields(att); var i = 0; var l = $asize(f); while( i < l ) { $objset(att,f[i], String.new($objget(att,f[i])) ); i = i + 1; } o.att = att; o.node = String.new(name); o.children = List.new(); o.parent = this.cur; this.cur.children.add(o); this.cur = o; } fpcdata = function(text) { var o = $new(null); $objsetproto(o,@Xml); o.text = String.new(text); o.parent = this.cur; o.cdata = false; this.cur.children.add(o); } fcdata = function(text) { var o = $new(); $objsetproto(o,@Xml); o.text = String.new(text); o.parent = this.cur; o.cdata = true; this.cur.children.add(o); } fcomment = function(comment) { } fdoctype = function(dtype) { } fdone = function() { this.cur = this.cur.parent; } Xml = $new(null); Xml.new = function(s) { var doc = $new(null); $objsetproto(doc,@Xml); doc.children = List.new(); parse_xml(s.@s,{ cur => doc, xml => fxml, cdata => fcdata, pcdata => fpcdata, comment => fcomment, doctype => fdoctype, done => fdone }); return doc; } @Xml = $new(null); Xml.prototype = @Xml; @Xml.get = function(name) { if( this.att == null ) $throw(String.new("Xml.get")); return $objget(this.att,$hash(name.@s)); } @Xml.set = function(name,val) { if( this.att == null ) $throw(String.new("Xml.set")); return $objset(this.att,$hash(name.@s),val); } @Xml.nodes = function() { if( this.children == null ) $throw(String.new("Xml.nodes")); var s = this.children.h; var l = List.new(); while( s != null ) { var x = s[0]; if( x.node != null ) l.add(x); s = s[1]; } return l; } @Xml.firstChild = function() { if( this.children == null ) $throw(String.new("Xml.firstChild")); return this.children.first() } @Xml.firstNode = function() { if( this.children == null ) $throw(String.new("Xml.firstNode")); var s = this.children.h; while( s != null ) { if( s[0].node != null ) return s[0]; s = s[1]; } return null; } @Xml.toString = function() { return String.new(this.__string()); } xml_string_rec = function(x,b) { if( x.text != null ) { if( x.cdata ) buffer_add(b,""); return; } var s = x.children; if( s == null ) return; s = s.h; if( x.node != null ) { buffer_add(b,"<"); buffer_add(b,x.node); var a = x.att; var f = $objfields(a); var n = $asize(f); var i = 0; while( i < n ) { buffer_add(b," "); buffer_add(b,$field(f[i])); buffer_add(b,"=\""); buffer_add(b,$objget(a,f[i])); buffer_add(b,"\""); i = i + 1; } if( s == null ) { buffer_add(b,"/>"); return; } buffer_add(b,">"); while( s != null ) { xml_string_rec(s[0],b); s = s[1]; } buffer_add(b,""); return; } while( s != null ) { xml_string_rec(s[0],b); s = s[1]; } } @Xml.__string = function() { var b = buffer_new(); xml_string_rec(this,b); return buffer_string(b); } @Xml.__serialize = function() { return $array("Xml",version); } $exports.Xml = Xml; $exports.__unserialize = function(v) { if( $typeof(v) != $tarray ) $throw("Invalid serialized data"); if( v[1] != version ) $throw("Invalid version for class "+v[0]); return switch( v[0] ) { "Xml" => @Xml "List" => @List "Array" => @Array "String" => @String "Hash" => @Hash default => $throw("Unknown class "+v[0]) } } neko-2-4-0/src/neko/000077500000000000000000000000001464615675700142175ustar00rootroot00000000000000neko-2-4-0/src/neko/Ast.nml000066400000000000000000000126331464615675700154630ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type pos = Lexer.pos type constant { True; False; Null; This; Int : int; Float : string; String : string; Builtin : string; Ident : string; } type keyword { Var; While; Do; If; Else; Function; Return; Break; Continue; Default; Try; Catch; Switch; } type token { Eof; Semicolon; Dot; Comma; Arrow; BraceOpen; BraceClose; ParentOpen; ParentClose; BracketOpen; BracketClose; Const : constant; Keyword : keyword; Binop : string; Comment : string; CommentLine : string; } type while_flag { NormalWhile; DoWhile; } type expr; type expr_decl { EConst : constant; EBlock : expr list; EParenthesis : expr; EField : (expr , string); ECall : (expr , expr list); EArray : (expr , expr); EVars : (string , expr option) list; EWhile : (expr , expr , while_flag); EIf : (expr , expr , expr option); ETry : (expr , string , expr); EFunction : (string list , expr); EBinop : (string , expr , expr); EReturn : expr option; EBreak : expr option; EContinue; ENext : (expr , expr); EObject : (string , expr) list; ELabel : string; ESwitch : (expr, (expr, expr) list, expr option); } type expr = (expr_decl , pos) function pos((_,x)) { x } var var_args = -1; function mk_call(v,args,p) { (ECall v args , p) } function mk_call0(v,p) { (ECall v [], p) } function mk_call1(v,a,p) { (ECall v [a], p) } function mk_ident(i,p) { (EConst (Ident i), p) } function mk_builtin(b,p) { (EConst (Builtin b), p) } function mk_int(i,p) { (EConst (Int i), p) } function mk_string(s,p) { (EConst (String s), p) } function mk_binop(op,e1,e2,p) { (EBinop op e1 e2, p) } function map(f,(e,p)) { (match e { | EBlock el -> EBlock (List.map f el) | EParenthesis e -> EParenthesis f(e) | EField (e,s) -> EField f(e) s | ECall (e,el) -> ECall f(e) (List.map f el) | EArray (e1,e2) -> EArray f(e1) f(e2) | EVars vl -> EVars(List.map(function((v,e)) { (v , match e { | None -> None | Some e -> Some (f e) }) },vl)) | EWhile (e1,e2,flag) -> EWhile f(e1) f(e2) flag | EIf (e,e1,e2) -> EIf f(e) f(e1) (match e2 { None -> None | Some e -> Some f(e)}) | ETry (e,ident,e2) -> ETry f(e) ident f(e2) | EFunction (params,e) -> EFunction params f(e) | EBinop (op,e1,e2) -> EBinop op f(e1) f(e2) | EReturn (Some e) -> EReturn Some(f e) | EBreak (Some e) -> EBreak Some(f e) | ENext (e1,e2) -> ENext f(e1) f(e2) | EObject fl -> EObject (List.map (function((s,e)) { (s , f e) }) fl) | ESwitch (e,cases,eo) -> ESwitch f(e) (List.map (function((e1,e2)) { (f e1, f e2) }) cases) (match eo { None -> None | Some e -> Some f(e) }) | EReturn None | EBreak None | EContinue | ELabel _ | EConst _ -> e }, p) } function iter(f,(e,p)) { match e { | EBlock el -> List.iter f el | EParenthesis e -> f e | EField (e,s) -> f e | ECall (e,el) -> f e; List.iter f el | EArray (e1,e2) -> f e1; f e2 | EVars vl -> List.iter (function((_,e)) { match e { None -> () | Some e -> f e } }) vl | EWhile (e1,e2,_) -> f e1; f e2 | EIf (e,e1,e2) -> f e; f e1; (match e2 { None -> () | Some e -> f e }) | ETry (e1,_,e2) -> f e1; f e2 | EFunction (_,e) -> f e | EBinop (_,e1,e2) -> f e1; f e2 | EReturn (Some e) -> f e | EBreak (Some e) -> f e | ENext (e1,e2) -> f e1; f e2 | EObject fl -> List.iter (function((_,e)) { f e }) fl | ESwitch (e,cases,eo) -> f e; List.iter (function((e1,e2)) { f e1; f e2 }) cases; match eo { None -> () | Some e -> f e }; | EReturn None | EBreak None | EContinue | ELabel _ | EConst _ -> () } } function s_constant(x) { match x { | True -> "true" | False -> "false" | Null -> "null" | This -> "this" | Int i -> string i | Float s -> s | String s -> "\"" + String.escape s + "\"" | Builtin s -> "$" + s | Ident s -> s } } function s_keyword(x) { match x { | Var -> "var" | While -> "while" | Do -> "do" | If -> "if" | Else -> "else" | Function -> "function" | Return -> "return" | Break -> "break" | Continue -> "continue" | Default -> "default" | Try -> "try" | Catch -> "catch" | Switch -> "switch" } } function s_token(x) { match x { | Eof -> "" | Semicolon -> ";" | Dot -> "." | Comma -> "," | Arrow -> "=>" | BraceOpen -> "{" | BraceClose -> "}" | ParentOpen -> "(" | ParentClose -> ")" | BracketOpen -> "[" | BracketClose -> "]" | Const c -> s_constant c | Keyword k -> s_keyword k | Binop s -> s | Comment s -> "/*" + s + "*/" | CommentLine s -> "//" + s } } neko-2-4-0/src/neko/Binast.nml000066400000000000000000000126601464615675700161540ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Lexer; open Neko.Ast; type context { ch : IO.input; strings : string array; mutable pos : Lexer.pos; } var parse_from_string : (string -> pos -> expr) ref = &(function(_,_) { assert() }); var header = "NBA\001"; function parse_string(ctx) { var index = IO.read_byte ctx.ch; if index == 0 then { var size = IO.read_ui16 ctx.ch; var str = IO.read ctx.ch size; Array.add ctx.strings str; str } else Array.get ctx.strings (Array.length ctx.strings - index) } function rec parse_list(ctx,f,n) { if n == 0 then [] else { var x = f ctx; x :: parse_list ctx f (n - 1); } } function parse_constant(ctx) { match IO.read_byte ctx.ch { | 0 -> True | 1 -> False | 2 -> Null | 3 -> This | 4 -> Int (IO.read_byte ctx.ch) | 5 -> Int (IO.read_i32 ctx.ch) | 6 -> Float (parse_string ctx) | 7 -> String (parse_string ctx) | 8 -> Builtin (parse_string ctx) | 9 -> Ident (parse_string ctx) | n -> error ("Invalid const tag : " + n) } } function parse_op(ctx) { match IO.read_byte ctx.ch { | 0 -> "+" | 1 -> "-" | 2 -> "/" | 3 -> "*" | 4 -> "%" | 5 -> "<<" | 6 -> ">>" | 7 -> ">>>" | 8 -> "|" | 9 -> "&" | 10 -> "^" | 11 -> "==" | 12 -> "!=" | 13 -> ">" | 14 -> ">=" | 15 -> "<" | 16 -> "<=" | 17 -> "=" | 18 -> "&&" | 19 -> "||" | 20 -> "++=" | 21 -> "--=" | 22 -> "+=" | 23 -> "-=" | 24 -> "/=" | 25 -> "*=" | 26 -> "%=" | 27 -> "<<=" | 28 -> ">>=" | 29 -> ">>>=" | 30 -> "|=" | 31 -> "&=" | 32 -> "^=" | n -> error ("Invalid op tag "+n) } } function rec parse_field(ctx) { var name = parse_string ctx; (name,parse_expr ctx) } function rec parse_switch(ctx,n) { var e = parse_expr ctx; var l = parse_list ctx (function(ctx) { var e1 = parse_expr ctx; var e2 = parse_expr ctx; (e1,e2) }) n; var eo = parse_expr_opt ctx; ESwitch e l eo } function rec parse_expr_opt(ctx) { if IO.read_byte ctx.ch == 0 then None else Some (parse_expr ctx) } function rec parse_expr(ctx) { var n = IO.read_byte ctx.ch; if n < 2 then { var file = if n == 1 then ctx.pos.psource else parse_string ctx; var line = IO.read_ui24 ctx.ch; ctx.pos := { psource = file; pline = line; pmin = 0; pmax = 0; }; parse_expr(ctx); } else { var pos = ctx.pos; (match n { | 2 -> EConst (parse_constant ctx) | 3 -> EBlock (parse_list ctx parse_expr (IO.read_byte ctx.ch)) | 4 -> EBlock (parse_list ctx parse_expr (IO.read_ui24 ctx.ch)) | 5 -> EParenthesis (parse_expr ctx) | 6 -> var e = parse_expr ctx; EField e (parse_string ctx) | 7 -> var e = parse_expr ctx; ECall e (parse_list ctx parse_expr (IO.read_byte ctx.ch)) | 8 -> var e = parse_expr ctx; EArray e (parse_expr ctx) | 9 -> function parse_var(_) { var v = parse_string ctx; var e = parse_expr_opt ctx; (v,e); } EVars (parse_list ctx parse_var (IO.read_byte ctx.ch)) | 10 -> var e1 = parse_expr ctx; var e2 = parse_expr ctx; EWhile e1 e2 NormalWhile | 11 -> var e1 = parse_expr ctx; var e2 = parse_expr ctx; EWhile e1 e2 DoWhile | 12 -> var e1 = parse_expr ctx; var e2 = parse_expr ctx; EIf e1 e2 (parse_expr_opt ctx) | 13 -> var e1 = parse_expr ctx; var str = parse_string ctx; ETry e1 str (parse_expr ctx) | 14 -> var vars = parse_list ctx parse_string (IO.read_byte ctx.ch); EFunction vars (parse_expr ctx) | 15 -> var op = parse_op ctx; var e1 = parse_expr ctx; var e2 = parse_expr ctx; EBinop op e1 e2 | 16 -> EReturn None | 17 -> EReturn Some(parse_expr ctx) | 18 -> EBreak None | 19 -> EBreak Some(parse_expr ctx) | 20 -> EContinue | 21 -> var e1 = parse_expr ctx; var e2 = parse_expr ctx; ENext e1 e2 | 22 -> EObject (parse_list ctx parse_field (IO.read_byte ctx.ch)) | 23 -> EObject (parse_list ctx parse_field (IO.read_ui24 ctx.ch)) | 24 -> ELabel (parse_string ctx) | 25 -> parse_switch ctx (IO.read_byte ctx.ch) | 26 -> parse_switch ctx (IO.read_ui24 ctx.ch) | 27 -> var str = IO.read ctx.ch (IO.read_ui24 ctx.ch); fst (*parse_from_string)(str,pos) | 28 -> var e = parse_expr ctx; ECall e (parse_list ctx parse_expr (IO.read_ui24 ctx.ch)) | n -> error ("Invalid expr tag : " + n) },pos) } } function parse(ch,pos) { var ctx = { ch = ch; strings = Array.create(); pos = pos; }; if IO.read ctx.ch 4 != header then error "Invalid binast header"; parse_expr(ctx); } neko-2-4-0/src/neko/Bytecode.nml000066400000000000000000000373751464615675700165040ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type opcode { // getters AccNull; AccTrue; AccFalse; AccThis; AccInt : int; AccStack : int; AccGlobal : int; AccEnv : int; AccField : string; AccArray; AccIndex : int; AccBuiltin : string; // setters SetStack : int; SetGlobal : int; SetEnv : int; SetField : string; SetArray; SetIndex : int; SetThis; // stack ops Push; Pop : int; Call : int; ObjCall : int; Jump : int; JumpIf : int; JumpIfNot : int; Trap : int; EndTrap; Ret : int; MakeEnv : int; MakeArray : int; // value ops Bool; IsNull; IsNotNull; Add; Sub; Mult; Div; Mod; Shl; Shr; UShr; Or; And; Xor; Eq; Neq; Gt; Gte; Lt; Lte; Not; // extra ops TypeOf; Compare; Hash; New; JumpTable : int; Apply : int; AccStack0; AccStack1; AccIndex0; AccIndex1; PhysCompare; TailCall : (int, int); Loop; }; type global { GlobalVar : string; GlobalFunction : (int , int); GlobalString : string; GlobalFloat : string; GlobalDebug : (string array, (int, int) array); GlobalVersion : int; } exception Invalid_file; var trap_stack_delta = 6 var inull : int = neko("null") function hash_field(s : string) : int { neko("$hash(s)") } function op_param(x) { match x { | AccInt _ | AccStack _ | AccGlobal _ | AccEnv _ | AccField _ | AccBuiltin _ | SetStack _ | SetGlobal _ | SetEnv _ | SetField _ | Pop _ | Call _ | ObjCall _ | Jump _ | JumpIf _ | JumpIfNot _ | JumpTable _ | Trap _ | MakeEnv _ | MakeArray _ | Ret _ | AccIndex _ | SetIndex _ | Apply _ | TailCall _ -> true | AccNull | AccTrue | AccFalse | AccThis | AccArray | SetArray | SetThis | Push | EndTrap | Bool | Add | Sub | Mult | Div | Mod | Shl | Shr | UShr | Or | And | Xor | Eq | Neq | Gt | Gte | Lt | Lte | IsNull | IsNotNull | Not | TypeOf | Compare | Hash | New | AccStack0 | AccStack1 | AccIndex0 | AccIndex1 | PhysCompare | Loop -> false } } function code_tables(ops) { var ids = Hashtbl.create(); var fids = Array.create(); Array.iter (function(x) { match x { | AccField s | SetField s | AccBuiltin s -> var id = hash_field s; try var f = Hashtbl.find ids id; if f != s then error("Field hashing conflict " + s + " and " + f); catch { Not_found -> Hashtbl.add ids id s; Array.add fids s } | _ -> () } }) ops; var p = &0; var pos = Array.make (Array.length(ops) + 1) 0; Array.iteri (function(i,op) { pos.[i] := *p; p := *p + (if op_param op then 2 else 1); }) ops; pos.[Array.length ops] := *p; (fids , pos , *p) } function write_debug_infos(ch,files,inf) { var nfiles = Array.length files; // the encoding of nfiles was set to keep // backward compatibility with 1.3 which // only allowed up to 127 filenames var lot_of_files = &false; if nfiles < 0x80 then IO.write_byte ch nfiles else if nfiles < 0x8000 then { lot_of_files := true; IO.write_byte ch ((nfiles >> 8) or 0x80); IO.write_byte ch (nfiles and 0xFF); } else invalid_arg(); Array.iter (function(s) { IO.write ch s; IO.write_char ch '\000' }) files; IO.write_i32 ch (Array.length inf); var curfile = &0; var curpos = &0; var rcount = &0; function rec flush_repeat(p) { if *rcount > 0 then { if *rcount > 15 then { IO.write_byte ch ((15 << 2) or 2); rcount := *rcount - 15; flush_repeat(p) } else { var delta = p - *curpos; var delta = (if delta > 0 && delta < 4 then delta else 0); IO.write_byte ch ((delta << 6) or (*rcount << 2) or 2); rcount := 0; curpos := *curpos + delta; } } } Array.iter (function((f,p)) { if f != *curfile then { flush_repeat(p); curfile := f; if *lot_of_files then { IO.write_byte ch ((f >> 7) or 1); IO.write_byte ch (f and 0xFF); } else IO.write_byte ch ((f << 1) or 1); } if p != *curpos then flush_repeat(p); if p == *curpos then rcount := *rcount + 1 else { var delta = p - *curpos; if delta > 0 && delta < 32 then { IO.write_byte ch ((delta << 3) or 4) } else { IO.write_byte ch (p << 3); IO.write_byte ch (p >> 5); IO.write_byte ch (p >> 13); } curpos := p; } }) inf; flush_repeat(*curpos) } function write(ch,(globals,ops)) { IO.write ch "NEKO"; var ids , pos , csize = code_tables ops; IO.write_i32 ch (Array.length globals); IO.write_i32 ch (Array.length ids); IO.write_i32 ch csize; Array.iter (function(x) { match x { | GlobalVar s -> IO.write_byte ch 1; IO.write ch s; IO.write_char ch '\000'; | GlobalFunction (p,nargs) -> IO.write_byte ch 2; IO.write_i32 ch (pos.[p] or (nargs << 24)) | GlobalString s -> IO.write_byte ch 3; IO.write_ui16 ch (String.length s); IO.write ch s | GlobalFloat s -> IO.write_byte ch 4; IO.write ch s; IO.write_char ch '\000' | GlobalDebug (files,inf) -> IO.write_byte ch 5; write_debug_infos ch files inf; | GlobalVersion v -> IO.write_byte ch 6; IO.write_byte ch v } }) globals; Array.iter (function(s) { IO.write ch s; IO.write_char ch '\000'; }) ids; Array.iteri (function(i,op) { var pop = &inull; var opid = (match op { | AccNull -> 0 | AccTrue -> 1 | AccFalse -> 2 | AccThis -> 3 | AccInt n -> pop := n; 4 | AccStack n -> pop := (n - 2); 5 | AccGlobal n -> pop := n; 6 | AccEnv n -> pop := n; 7 | AccField s -> pop := (hash_field s); 8 | AccArray -> 9 | AccIndex n -> pop := (n - 2); 10 | AccBuiltin s -> pop := (hash_field s); 11 | SetStack n -> pop := n; 12 | SetGlobal n -> pop := n; 13 | SetEnv n -> pop := n; 14 | SetField s -> pop := (hash_field s); 15 | SetArray -> 16 | SetIndex n -> pop := n; 17 | SetThis -> 18 | Push -> 19 | Pop n -> pop := n; 20 | Call n -> pop := n; 21 | ObjCall n -> pop := n; 22 | Jump n -> pop := (pos.[i+n] - pos.[i]); 23 | JumpIf n -> pop := (pos.[i+n] - pos.[i]); 24 | JumpIfNot n -> pop := (pos.[i+n] - pos.[i]); 25 | Trap n -> pop := (pos.[i+n] - pos.[i]); 26 | EndTrap -> 27 | Ret n -> pop := n; 28 | MakeEnv n -> pop := n; 29 | MakeArray n -> pop := n; 30 | Bool -> 31 | IsNull -> 32 | IsNotNull -> 33 | Add -> 34 | Sub -> 35 | Mult -> 36 | Div -> 37 | Mod -> 38 | Shl -> 39 | Shr -> 40 | UShr -> 41 | Or -> 42 | And -> 43 | Xor -> 44 | Eq -> 45 | Neq -> 46 | Gt -> 47 | Gte -> 48 | Lt -> 49 | Lte -> 50 | Not -> 51 | TypeOf -> 52 | Compare -> 53 | Hash -> 54 | New -> 55 | JumpTable n -> pop := n; 56 | Apply n -> pop := n; 57 | AccStack0 -> 58 | AccStack1 -> 59 | AccIndex0 -> 60 | AccIndex1 -> 61 | PhysCompare -> 62 | TailCall (args,st) -> pop := (args or (st << 3)); 63 | Loop -> pop := 64; 0 }); var n = *pop; if n == inull then IO.write_byte ch (opid << 2) else if opid < 32 && (n == 0 || n == 1) then IO.write_byte ch ((opid << 3) or (n << 2) or 1) else if n >= 0 && n <= 0xFF then { IO.write_byte ch ((opid << 2) or 2); IO.write_byte ch n; } else { IO.write_byte ch ((opid << 2) or 3); IO.write_i32 ch n; } }) ops } function read_string(ch) { var b = Buffer.create(); function rec loop() { var c = IO.read_char ch; if c == '\000' then Buffer.string b else { Buffer.add_char b c; loop() } }; loop() } function read_debug_infos(ch) { var nfiles = IO.read_byte ch; // see comments in read_debug_infos var lot_of_files = &false; var nfiles = if nfiles < 0x80 then nfiles else { lot_of_files := true; var b = IO.read_byte ch; ((nfiles and 0x7F) << 8) or b }; if nfiles == 0 then invalid_arg(); var files = Array.init nfiles (function(_) { read_string ch }); var npos = IO.read_i32 ch; var curfile = &0; var curpos = &0; var pos = Array.make npos (0,0); function rec loop(i) { if i == npos then () else { var b = IO.read_byte ch; if b and 1 != 0 then { var file = if *lot_of_files then { var b2 = IO.read_byte ch; ((b >> 1) << 8) or b2 } else b >> 1; if file >= Array.length files then invalid_arg(); curfile := file; loop(i) } else if b and 2 != 0 then { var delta = b >> 6; var count = (b >> 2) and 15; var p = &0; while *p < count { pos.[i + *p] := (*curfile,*curpos); p := *p + 1; } curpos := *curpos + delta; loop (i + count) } else if b and 4 != 0 then { curpos := *curpos + (b >> 3); pos.[i] := (*curfile,*curpos); loop (i + 1) } else { var b2 = IO.read_byte ch; var b3 = IO.read_byte ch; curpos := (b >> 3) or (b2 << 5) or (b3 << 13); pos.[i] := (*curfile,*curpos); loop (i + 1) } } } loop 0; (files, pos) } function read(ch) { try { var head = IO.read ch 4; if head != "NEKO" then throw Invalid_file; var nglobals = IO.read_i32 ch; var nids = IO.read_i32 ch; var csize = IO.read_i32 ch; if nglobals < 0 || nglobals > 0xFFFF || nids < 0 || nids > 0xFFFF || csize < 0 || csize > 0xFFFFFF then throw Invalid_file; var globals = Array.init nglobals (function(_) { match IO.read_byte ch { | 1 -> GlobalVar(read_string ch) | 2 -> var v = IO.read_i32 ch; GlobalFunction(v and 0xFFFFFF, v >> 24) | 3 -> var len = IO.read_ui16 ch; GlobalString(IO.read ch len) | 4 -> GlobalFloat(read_string ch) | 5 -> var files, inf = read_debug_infos ch; GlobalDebug files inf | 6 -> GlobalVersion(IO.read_byte ch) | _ -> throw Invalid_file } }); var ids = Hashtbl.create(); function rec loop(n) { if n == 0 then () else { var s = read_string ch; var id = hash_field s; try var s2 = Hashtbl.find ids id; if s != s2 then throw Invalid_file; catch { Not_found -> Hashtbl.add ids id s; loop (n-1) } } }; loop nids; var pos = Array.make (csize+1) (-1); var cpos = &0; var jumps = &[]; var ops = Array.create(); while *cpos < csize { var code = IO.read_byte ch; var op , p = match code and 3 { | 0 -> (code >> 2 , inull) | 1 -> (code >> 3 , ((code >> 2) and 1)) | 2 -> if code == 2 then (IO.read_byte ch, inull) else (code >> 2 , IO.read_byte ch) | 3 -> (code >> 2 , IO.read_i32 ch) | _ -> assert() }; var op = match op { | 0 -> AccNull | 1 -> AccTrue | 2 -> AccFalse | 3 -> AccThis | 4 -> AccInt p | 5 -> AccStack (p + 2) | 6 -> AccGlobal p | 7 -> AccEnv p | 8 -> AccField (try Hashtbl.find ids p catch { Not_found -> throw Invalid_file }) | 9 -> AccArray | 10 -> AccIndex (p + 2) | 11 -> AccBuiltin (try Hashtbl.find ids p catch { Not_found -> throw Invalid_file }) | 12 -> SetStack p | 13 -> SetGlobal p | 14 -> SetEnv p | 15 -> SetField (try Hashtbl.find ids p catch { Not_found -> throw Invalid_file }) | 16 -> SetArray | 17 -> SetIndex p | 18 -> SetThis | 19 -> Push | 20 -> Pop p | 21 -> Call p | 22 -> ObjCall p | 23 -> jumps := (*cpos , Array.length ops) :: *jumps; Jump p | 24 -> jumps := (*cpos , Array.length ops) :: *jumps; JumpIf p | 25 -> jumps := (*cpos , Array.length ops) :: *jumps; JumpIfNot p | 26 -> jumps := (*cpos , Array.length ops) :: *jumps; Trap p | 27 -> EndTrap | 28 -> Ret p | 29 -> MakeEnv p | 30 -> MakeArray p | 31 -> Bool | 32 -> IsNull | 33 -> IsNotNull | 34 -> Add | 35 -> Sub | 36 -> Mult | 37 -> Div | 38 -> Mod | 39 -> Shl | 40 -> Shr | 41 -> UShr | 42 -> Or | 43 -> And | 44 -> Xor | 45 -> Eq | 46 -> Neq | 47 -> Gt | 48 -> Gte | 49 -> Lt | 50 -> Lte | 51 -> Not | 52 -> TypeOf | 53 -> Compare | 54 -> Hash | 55 -> New | 56 -> JumpTable p | 57 -> Apply p | 58 -> AccStack0 | 59 -> AccStack1 | 60 -> AccIndex0 | 61 -> AccIndex1 | 62 -> PhysCompare | 63 -> TailCall (p and 7) (p >> 3) | 64 -> Loop | _ -> throw Invalid_file }; pos.[*cpos] := Array.length ops; cpos := *cpos + (if op_param op then 2 else 1); Array.add ops op; }; if *cpos != csize then throw Invalid_file; pos.[*cpos] := Array.length ops; function pos_index(i,sadr) { var idx = pos.[sadr]; if idx == -1 then throw Invalid_file; idx - i }; List.iter (function((a,i)) { Array.set ops i (match Array.get ops i { | Jump p -> Jump (pos_index i (a+p)) | JumpIf p -> JumpIf (pos_index i (a+p)) | JumpIfNot p -> JumpIfNot (pos_index i (a+p)) | Trap p -> Trap (pos_index i (a+p)) | _ -> assert() }); }) (*jumps); Array.iteri (function(i,g) { match g { | GlobalFunction(f,n) -> globals.[i] := GlobalFunction(pos_index 0 f,n) | _ -> () } }) globals; (globals , ops) } catch { | IO.Eof | IO.Overflow _ -> throw Invalid_file } } function dump(ch,(globals,ops)) { var ids, pos , csize = code_tables ops; IO.printf ch "nglobals : %d\n" (Array.length globals); IO.printf ch "nfields : %d\n" (Array.length ids); IO.printf ch "codesize : %d ops , %d total\n" (Array.length ops,csize); IO.printf ch "GLOBALS =\n" (); var marks = Array.make csize false; Array.iteri (function(i,g) { IO.printf ch " global %d : %s\n" (i, match g { | GlobalVar s -> "var " + String.escape s | GlobalFunction (p,n) -> if p >= 0 && p < csize then marks.[p] := true; "function " + string p + " nargs " + string n | GlobalString s -> "string \"" + String.escape s + "\"" | GlobalFloat s -> "float " + String.escape s | GlobalDebug (files,inf) -> var ch, buf = IO.write_string(); write_debug_infos ch files inf; sprintf "debug %d ops %d bytes" (Array.length inf,String.length buf()) | GlobalVersion v -> "version " + string v }) }) globals; IO.printf ch "FIELDS =\n" (); Array.iter (function(f) { IO.printf ch " %s%s%.8X\n" (f , if String.length(f) >= 24 then " " else String.make (24 - String.length f) ' ', hash_field f); }) ids; IO.printf ch "CODE =\n" (); function str(s,i) { s + " " + string i }; var bpos = &0; Array.iteri (function(pos,op) { if marks.[pos] then IO.write_char ch '\n'; IO.printf ch "%.6X %6d %s\n" (*bpos , pos , match op { | AccInt i -> str "AccInt" i | AccStack i -> str "AccStack" i | AccGlobal i -> str "AccGlobal" i | AccEnv i -> str "AccEnv" i | AccField s -> "AccField " + s | AccIndex i -> str "AccIndex" i | AccBuiltin s -> "AccBuiltin " + s | SetStack i -> str "SetStack" i | SetGlobal i -> str "SetGlobal" i | SetEnv i -> str "SetEnv" i | SetField f -> "SetField " + f | SetIndex i -> str "SetIndex" i | Pop i -> str "Pop" i | Call i -> str "Call" i | ObjCall i -> str "ObjCall" i | Jump i -> str "Jump" (pos + i) | JumpIf i -> str "JumpIf" (pos + i) | JumpIfNot i -> str "JumpIfNot" (pos + i) | Trap i -> str "Trap" (pos + i) | Ret i -> str "Ret" i | MakeEnv i -> str "MakeEnv" i | MakeArray i -> str "MakeArray" i | JumpTable i -> str "JumpTable" i | Apply i -> str "Apply" i | _ -> string op }); bpos := *bpos + if op_param op then 2 else 1; }) ops; IO.printf ch "END\n" (); } neko-2-4-0/src/neko/Compile.nml000066400000000000000000000655431464615675700163340ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Lexer; open Neko.Ast; open Neko.Bytecode; type access { XEnv : int; XStack : int; XGlobal : int; XField : string; XIndex : int; XArray; XThis; } type label { lname : string; ltraps : int list; lstack : int; mutable lpos : int option; mutable lwait : (void -> void) list; } type globals { globals : (global,int) Hashtbl.t; gobjects : (string list,int) Hashtbl.t; mutable functions : (opcode array, (int,int) array, int , int) list; mutable gtable : global array; labels : (string,label) Hashtbl.t; hfiles : (string,int) Hashtbl.t; files : string array; } type context { g : globals; version : int; mutable ops : opcode array; mutable locals : (string,int) Map.t; mutable env : (string,int) Map.t; mutable nenv : int; mutable stack : int; mutable loop_limit : int; mutable loop_traps : int; mutable limit : int; mutable traps : int list; mutable breaks : (void -> void, pos) list; mutable continues : (void -> void, pos) list; mutable pos : (int,int) array; mutable curpos : (int, int); mutable curfile : string; } type error_msg = string exception Error : (error_msg , pos) function error(e,p) { throw Error(e,p) } function error_msg(s) { s } function stack_delta(o) { match o { | AccNull | AccTrue | AccFalse | AccThis | AccInt _ | AccStack _ | AccGlobal _ | AccEnv _ | AccField _ | AccBuiltin _ | AccIndex _ | JumpIf _ | JumpIfNot _ | Jump _ | JumpTable _ | Ret _ | SetGlobal _ | SetStack _ | SetEnv _ | SetThis | Bool | IsNull | IsNotNull | Not | Hash | TypeOf | New | AccStack0 | AccStack1 | AccIndex0 | AccIndex1 | Loop -> 0 | Add | Sub | Mult | Div | Mod | Shl | Shr | UShr | Or | And | Xor | Eq | Neq | Gt | Gte | Lt | Lte | PhysCompare -> -1 | AccArray -> -1 | SetField _ | SetIndex _ | Compare -> -1 | SetArray -> -2 | Push -> 1 | Pop x -> -x | Apply nargs | Call nargs | TailCall (nargs,_) -> -nargs | ObjCall nargs -> -(nargs + 1) | MakeEnv size | MakeArray size -> -size | Trap _ -> trap_stack_delta | EndTrap -> -trap_stack_delta } } function check_stack(ctx,stack,p) { if ctx.stack != stack then error "Stack alignment failure" p; } function pos(ctx) { Array.length ctx.ops } function set_pos(ctx,p) { if p.psource == ctx.curfile then { if p.pline != snd ctx.curpos then ctx.curpos := (fst ctx.curpos, p.pline); } else if p == Lexer.null_pos then { // nothing } else { var fid = try Hashtbl.find ctx.g.hfiles p.psource catch { Not_found -> var fid = Array.length ctx.g.files; Array.add ctx.g.files p.psource; Hashtbl.add ctx.g.hfiles p.psource fid; fid; } ctx.curfile := p.psource; ctx.curpos := (fid,p.pline); } } function write(ctx,op) { ctx.stack := ctx.stack + stack_delta op; Array.add ctx.pos ctx.curpos; if op_param op then Array.add ctx.pos ctx.curpos; Array.add ctx.ops op; } function jmp(ctx) { var p = pos ctx; write ctx (Jump 0); function() { Array.set ctx.ops p Jump(pos ctx - p) } } function cjmp(cond,ctx) { var p = pos ctx; write ctx (Jump 0); function() { Array.set ctx.ops p (if cond then JumpIf else JumpIfNot)(pos ctx - p) } } function trap(ctx) { var p = pos ctx; write ctx (Trap 0); function() { Array.set ctx.ops p Trap(pos ctx - p) } } function goto(ctx,p) { write ctx Jump(p - pos ctx) } function global(ctx,g) { var ginf = ctx.g; try Hashtbl.find ginf.globals g catch { Not_found -> var gid = Array.length ginf.gtable; Hashtbl.add ginf.globals g gid; Array.add ginf.gtable g; gid } } function save_breaks(ctx) { var oldc = ctx.continues; var oldb = ctx.breaks; var oldl = ctx.loop_limit; var oldt = ctx.loop_traps; ctx.loop_limit := ctx.stack; ctx.breaks := []; ctx.continues := []; (ctx , oldc, oldb , oldl, oldt) } function process_continues((ctx,oldc,_,_,_)) { List.iter (function((f,_)) { f() }) ctx.continues; ctx.continues := oldc } function process_breaks((ctx,_,oldb,oldl,oldt)) { List.iter (function((f,_)) { f() }) ctx.breaks; ctx.loop_limit := oldl; ctx.loop_traps := oldt; ctx.breaks := oldb } function check_breaks(ctx) { List.iter (function((_,p)) { error "Break outside a loop" p }) ctx.breaks; List.iter (function((_,p)) { error "Continue outside a loop" p }) ctx.continues } function make_array(p,el) { (ECall (EConst (Builtin "array"),p) el , p) } function get_cases_ints(cases) { var max = &(-1); var l = List.map (function((e,e2)) { match e { | (EConst (Int n),_) when n >= 0 -> if n > *max then max := n; (n,e2) | _ -> throw Exit } }) cases; // only create jump table if small or >10% cases matched var nmatches = List.length l; if nmatches < 3 then throw Exit; if *max >= 16 && (nmatches * 100) / (*max + 1) < 10 then throw Exit; if *max > 512 then throw Exit; (l,*max + 1) } function rec scan_labels(ctx,supported,in_block,e) { match fst e { | EFunction (args,e) -> var nargs = List.length args; var traps = ctx.traps; ctx.traps := []; ctx.stack := ctx.stack + nargs; scan_labels ctx supported false e; ctx.stack := ctx.stack - nargs; ctx.traps := traps | EBlock _ -> var old = ctx.stack; Neko.Ast.iter (scan_labels ctx supported true) e; ctx.stack := old | EVars l -> if !in_block then error "Variable declaration must be done inside a block" (snd e); List.iter (function((_,e)) { match e { | None -> () | Some e -> scan_labels ctx supported false e }; ctx.stack := ctx.stack + 1 }) l | ELabel l when !supported -> error "Label is not supported in this part of the program" (snd e); | ELabel l when Hashtbl.exists ctx.g.labels l -> error ("Duplicate label " + l) (snd e) | ELabel l -> var label = { lname = l; ltraps = List.rev ctx.traps; lstack = ctx.stack; lpos = None; lwait = []; }; Hashtbl.add ctx.g.labels l label | ETry (e,_,e2) -> ctx.stack := ctx.stack + trap_stack_delta; ctx.traps := ctx.stack :: ctx.traps; scan_labels ctx supported false e; ctx.stack := ctx.stack - trap_stack_delta; ctx.traps := match ctx.traps { [] -> assert() | _ :: l -> l }; ctx.stack := ctx.stack + 1; scan_labels ctx supported false e2; ctx.stack := ctx.stack - 1; | EBinop ("=",e1,e2) -> function rec is_extended((e,_)) { match e { | EParenthesis e -> is_extended e | EArray _ | EField _ -> true | _ -> false } }; var ext = is_extended e1; if ext then ctx.stack := ctx.stack + 1; scan_labels ctx supported false e2; ctx.stack := ctx.stack + 1; scan_labels ctx supported false e1; ctx.stack := ctx.stack - (if ext then 2 else 1); | ECall ((EConst (Builtin "array"),_),e :: el) -> if ctx.version >= 2 then { scan_labels ctx supported false e; List.iter (function(e) { ctx.stack := ctx.stack + 1; scan_labels ctx supported false e; }) el; ctx.stack := ctx.stack - List.length el } else { List.iter (function(e) { scan_labels ctx supported false e; ctx.stack := ctx.stack + 1; }) el; scan_labels ctx supported false e; ctx.stack := ctx.stack - List.length el } | ECall ((EConst (Builtin x),_),el) when x != "apply" -> Neko.Ast.iter (scan_labels ctx false false) e | ECall ((EConst (Builtin "apply"),_),e :: el) | ECall(e,el) -> List.iter (function(e) { scan_labels ctx supported false e; ctx.stack := ctx.stack + 1; }) el; scan_labels ctx supported false e; ctx.stack := ctx.stack - List.length el | EObject fl -> ctx.stack := ctx.stack + 2; List.iter (function((s,e)) { scan_labels ctx supported false e }) fl; ctx.stack := ctx.stack - 2; | ESwitch (ee,[(econd,exec)],eo) -> var p = snd e; scan_labels ctx supported false (EIf (EBinop "==" ee econd,p) exec eo,p) | ESwitch (e,cases,eo) -> scan_labels ctx supported false e; var delta = try { ignore(get_cases_ints cases); 0 } catch { Exit -> 1 }; ctx.stack := ctx.stack + delta; List.iter (function((e1,e2)) { ctx.stack := ctx.stack + delta; scan_labels ctx supported false e1; ctx.stack := ctx.stack - delta; scan_labels ctx supported false e2; }) cases; match eo { | None -> () | Some e -> scan_labels ctx supported false e } ctx.stack := ctx.stack - delta; | ENext (e1,e2) -> scan_labels ctx supported in_block e1; scan_labels ctx supported in_block e2; | EConst _ | EContinue | EBreak _ | EReturn _ | EIf _ | EWhile _ | EParenthesis _ -> Neko.Ast.iter (scan_labels ctx supported false) e | EBinop (_,_,_) | EArray _ | EField _ -> Neko.Ast.iter (scan_labels ctx false false) e } } function compile_constant(ctx,c,p) { match c { | True -> write ctx AccTrue | False -> write ctx AccFalse | Null -> write ctx AccNull | This -> write ctx AccThis | Int n -> write ctx (AccInt n) | Float f -> write ctx (AccGlobal (global ctx (GlobalFloat f))) | String s -> write ctx (AccGlobal (global ctx (GlobalString s))) | Builtin s -> match s { | "tnull" -> write ctx (AccInt 0) | "tint" -> write ctx (AccInt 1) | "tfloat" -> write ctx (AccInt 2) | "tbool" -> write ctx (AccInt 3) | "tstring" -> write ctx (AccInt 4) | "tobject" -> write ctx (AccInt 5) | "tarray" -> write ctx (AccInt 6) | "tfunction" -> write ctx (AccInt 7) | "tabstract" -> write ctx (AccInt 8) | s -> write ctx (AccBuiltin s) } | Ident s -> try { var l = Map.find ctx.locals s; if l <= ctx.limit then { var e = try { Map.find ctx.env s } catch { Not_found -> var e = ctx.nenv; ctx.nenv := ctx.nenv + 1; ctx.env := Map.add ctx.env s e; e }; write ctx (AccEnv e); } else { var p = ctx.stack - l; write ctx (if p == 0 then AccStack0 else if p == 1 then AccStack1 else AccStack p); } } catch { Not_found -> var g = global ctx (GlobalVar s); write ctx (AccGlobal g) } } } function rec compile_access(ctx,e) { match fst e { | EConst (Ident s) -> try { var l = Map.find ctx.locals s; if l <= ctx.limit then { var e = try { Map.find ctx.env s } catch { Not_found -> var e = ctx.nenv; ctx.nenv := ctx.nenv + 1; ctx.env := Map.add ctx.env s e; e }; XEnv e } else XStack l } catch { Not_found -> var g = global ctx (GlobalVar s); XGlobal g } | EField (e,f) -> compile ctx false e; write ctx Push; XField f | EArray (e1,(EConst (Int n),_)) -> compile ctx false e1; write ctx Push; XIndex n | EArray (ea,ei) -> compile ctx false ei; write ctx Push; compile ctx false ea; write ctx Push; XArray | EConst This -> XThis | _ -> error "Invalid access" (snd e) } } function rec compile_access_set(ctx,a) { match a { | XEnv n -> write ctx (SetEnv n) | XStack l -> write ctx (SetStack (ctx.stack - l)) | XGlobal g -> write ctx (SetGlobal g) | XField f -> write ctx (SetField f) | XIndex i -> write ctx (SetIndex i) | XThis -> write ctx SetThis | XArray -> write ctx SetArray } } function rec compile_access_get(ctx,a) { match a { | XEnv n -> write ctx (AccEnv n) | XStack l -> write ctx (AccStack (ctx.stack - l)) | XGlobal g -> write ctx (AccGlobal g) | XField f -> write ctx (AccField f) | XIndex i -> write ctx (AccIndex i) | XThis -> write ctx AccThis | XArray -> write ctx Push; write ctx (AccStack 2); write ctx AccArray } } function rec write_op(ctx,op,p) { match op { | "+" -> write ctx Add | "-" -> write ctx Sub | "/" -> write ctx Div | "*" -> write ctx Mult | "%" -> write ctx Mod | "<<" -> write ctx Shl | ">>" -> write ctx Shr | ">>>" -> write ctx UShr | "|" -> write ctx Or | "&" -> write ctx And | "^" -> write ctx Xor | "==" -> write ctx Eq | "!=" -> write ctx Neq | ">" -> write ctx Gt | ">=" -> write ctx Gte | "<" -> write ctx Lt | "<=" -> write ctx Lte | _ -> error "Unknown operation" p } } function rec compile_binop(ctx,tail,op,e1,e2,p) { match op { | "=" -> var a = compile_access ctx e1; compile ctx false e2; compile_access_set ctx a | "&&" -> compile ctx false e1; var jnext = cjmp false ctx; compile ctx tail e2; jnext() | "||" -> compile ctx false e1; var jnext = cjmp true ctx; compile ctx tail e2; jnext() | "++=" | "--=" -> write ctx Push; var base = ctx.stack; var a = compile_access ctx e1; compile_access_get ctx a; write ctx SetStack(ctx.stack - base); write ctx Push; compile ctx false e2; write_op ctx (String.sub op 0 (String.length op - 2)) p; compile_access_set ctx a; write ctx (AccStack 0); write ctx (Pop 1); | "+=" | "-=" | "/=" | "*=" | "%=" | "<<=" | ">>=" | ">>>=" | "|=" | "&=" | "^=" -> var a = compile_access ctx e1; compile_access_get ctx a; write ctx Push; compile ctx false e2; write_op ctx (String.sub op 0 (String.length op - 1)) p; compile_access_set ctx a | _ -> match (op , e1 , e2) { | ("==" , _ , (EConst Null,_)) -> compile ctx false e1; write ctx IsNull | ("!=" , _ , (EConst Null,_)) -> compile ctx false e1; write ctx IsNotNull | ("==" , (EConst Null,_) , _) -> compile ctx false e2; write ctx IsNull | ("!=" , (EConst Null,_) , _) -> compile ctx false e2; write ctx IsNotNull | ("-", (EConst (Int 0),_) , (EConst (Int i),_)) -> compile ctx tail (EConst (Int (-i)),p) | _ -> compile ctx false e1; write ctx Push; compile ctx false e2; write_op ctx op p } } } function rec compile_function(main,params,e) { var ctx = { g = main.g; // reset ops = Array.create(); pos = Array.create(); breaks = []; continues = []; env = Map.empty(); nenv = 0; traps = []; loop_traps = 0; limit = main.stack; // dup version = main.version; stack = main.stack; locals = main.locals; loop_limit = main.loop_limit; curpos = main.curpos; curfile = main.curfile; }; List.iter (function(v) { ctx.stack := ctx.stack + 1; ctx.locals := Map.add ctx.locals v ctx.stack; }) params; var s = ctx.stack; compile ctx true e; write ctx (Ret (ctx.stack - ctx.limit)); check_stack ctx s (snd e); check_breaks ctx; // add function var gid = Array.length ctx.g.gtable; ctx.g.functions := (ctx.ops,ctx.pos,gid,List.length params) :: ctx.g.functions; Array.add ctx.g.gtable GlobalFunction(gid,-1); // environment if ctx.nenv > 0 then { var a = Array.make ctx.nenv ""; Map.iter (function(v,i){ a.[i] := v }) ctx.env; Array.iter (function(v){ compile_constant main (Ident v) snd(e); write main Push; }) a; write main (AccGlobal gid); write main (MakeEnv ctx.nenv); } else write main (AccGlobal gid); } function rec compile_builtin(ctx,tail,b,el,p) { match (b , el) { | ("istrue" , [e]) -> compile ctx false e; write ctx Bool | ("not" , [e]) -> compile ctx false e; write ctx Not | ("typeof" , [e]) -> compile ctx false e; write ctx TypeOf | ("hash" , [e]) -> compile ctx false e; write ctx Hash | ("new" , [e]) -> compile ctx false e; write ctx New | ("compare" , [e1;e2]) -> compile ctx false e1; write ctx Push; compile ctx false e2; write ctx Compare | ("pcompare" , [e1;e2]) -> compile ctx false e1; write ctx Push; compile ctx false e2; write ctx PhysCompare | ("goto" , [(EConst (Ident l) , _)] ) -> var l = try Hashtbl.find ctx.g.labels l catch { Not_found -> error ("Unknown label " + l) p }; var os = ctx.stack; function rec loop(l1,l2) { match (l1,l2) { | (x :: l1 , y :: l2) when x == y -> loop l1 l2 | _ -> (l1,l2) } } var straps , dtraps = loop List.rev(ctx.traps) l.ltraps; List.iter (function(l) { if ctx.stack != l then write ctx Pop(ctx.stack - l); write ctx EndTrap; }) List.rev(straps); var dtraps = List.map (function(l) { var l = l - trap_stack_delta; if l < ctx.stack then write ctx Pop(ctx.stack - l); while ctx.stack < l { write ctx Push; } trap ctx }) dtraps; if l.lstack < ctx.stack then write ctx Pop(ctx.stack - l.lstack); while l.lstack > ctx.stack { write ctx Push }; ctx.stack := os; match l.lpos { | None -> l.lwait := jmp ctx :: l.lwait | Some p -> write ctx (Jump p) }; List.iter (function(t) { t(); write ctx Push; compile_constant ctx (Builtin "throw") p; write ctx (Call 1); // insert an infinite loop in order to // comply with bytecode checker ignore(jmp ctx) }) dtraps; | ("goto" , _) -> error "Invalid $goto statement" p | ("array",e :: el) -> var count = List.length el; // a single function can't have >128 stack if count > 120 - ctx.stack && count > 8 then { // split in 8 and recurse var part = count >> 3; function rec loop(el,acc,count) { match el { | [] -> [List.rev acc] | e :: l -> if count == part then List.rev(acc) :: loop el [] 0 else loop l (e :: acc) (count + 1) } } var arr = make_array p (List.map make_array(p) (loop (e :: el) [] 0)); compile_builtin ctx tail "aconcat" [arr] p; } else if ctx.version >= 2 then { compile ctx false e; List.iter (function(e) { write ctx Push; compile ctx false e; }) el; write ctx (MakeArray count); } else { List.iter (function(e) { compile ctx false e; write ctx Push; }) el; compile ctx false e; write ctx (MakeArray count); } | ("apply",e :: el) -> List.iter (function(e) { compile ctx false e; write ctx Push; }) el; compile ctx false e; var nargs = List.length el; if nargs > 0 then write ctx (Apply nargs); | _ -> List.iter (function(e) { compile ctx false e; write ctx Push; }) el; compile_constant ctx (Builtin b) p; if tail then write ctx TailCall(List.length el,ctx.stack - ctx.limit) else write ctx Call(List.length el) } } function rec compile(ctx,tail,(e,p)) { set_pos ctx p; match e { | EConst c -> compile_constant ctx c p | EBlock [] -> write ctx AccNull | EBlock el -> var locals = ctx.locals; var stack = ctx.stack; function rec loop(el) { match el { | [] -> assert() | [e] -> compile ctx tail e | [e; (ELabel _,_) as f] -> compile ctx tail e; compile ctx tail f | e :: el -> compile ctx false e; loop el } } loop el; if stack < ctx.stack then write ctx (Pop (ctx.stack - stack)); check_stack ctx stack p; ctx.locals := locals | EParenthesis e -> compile ctx tail e | EField (e,f) -> compile ctx false e; write ctx (AccField f) | ECall (e,a :: b :: c :: d :: x1 :: x2 :: l) when match e { (EConst (Builtin "array"),_) -> false | _ -> true } -> var call = (EConst (Builtin "call"),p); var args = (ECall (EConst (Builtin "array"),p) (a :: b :: c :: d :: x1 :: x2 :: l),p); match e { | (EField (e,name) , p2) -> var locals = ctx.locals; var etmp = (EConst (Ident "$tmp"),p2); compile ctx false (EVars [("$tmp",Some e)],p2); compile ctx tail (ECall call [(EField etmp name,p2);etmp;args], p); write ctx (Pop 1); ctx.locals := locals | _ -> compile ctx tail (ECall call [e; (EConst This,p); args],p); } | ECall ((EConst (Builtin b),_),el) -> compile_builtin ctx tail b el p | ECall ((EField (e,f),_),el) -> List.iter (function(e) { compile ctx false e; write ctx Push; }) el; compile ctx false e; write ctx Push; write ctx (AccField f); write ctx ObjCall(List.length el) | ECall (e,el) -> List.iter (function(e) { compile ctx false e; write ctx Push; }) el; compile ctx false e; if tail then write ctx TailCall(List.length el,ctx.stack - ctx.limit) else write ctx Call(List.length el) | EArray (e1,(EConst (Int n),_)) -> compile ctx false e1; write ctx (if n == 0 then AccIndex0 else if n == 1 then AccIndex1 else AccIndex n) | EArray (e1,e2) -> compile ctx false e1; write ctx Push; compile ctx false e2; write ctx AccArray | EVars vl -> List.iter (function((v,o)) { match o { | None -> write ctx AccNull | Some e -> compile ctx false e }; write ctx Push; ctx.locals := Map.add ctx.locals v ctx.stack; }) vl | EWhile (econd,e,NormalWhile) -> var start = pos ctx; if ctx.version >= 2 then write ctx Loop; compile ctx false econd; var jend = cjmp false ctx; var save = save_breaks ctx; compile ctx false e; process_continues save; goto ctx start; process_breaks save; jend(); | EWhile (econd,e,DoWhile) -> var start = pos ctx; if ctx.version >= 2 then write ctx Loop; var save = save_breaks ctx; compile ctx false e; process_continues save; compile ctx false econd; write ctx (JumpIf (start - pos ctx)); process_breaks save | EIf (e,e1,e2) -> var stack = ctx.stack; compile ctx false e; var jelse = cjmp false ctx; compile ctx tail e1; check_stack ctx stack p; match e2 { | None -> jelse() | Some e2 -> var jend = jmp ctx; jelse(); compile ctx tail e2; check_stack ctx stack p; jend() }; | ETry (e,v,ecatch) -> var trap = trap ctx; ctx.traps := ctx.stack :: ctx.traps; compile ctx false e; write ctx EndTrap; ctx.traps := match ctx.traps { [] -> assert() | _ :: l -> l }; var jend = jmp ctx; trap(); write ctx Push; var locals = ctx.locals; ctx.locals := Map.add ctx.locals v ctx.stack; compile ctx tail ecatch; write ctx (Pop 1); ctx.locals := locals; jend() | EBinop (op,e1,e2) -> compile_binop ctx tail op e1 e2 p | EReturn e -> match e { None -> write ctx AccNull | Some e -> compile ctx (ctx.traps == []) e }; var stack = ctx.stack; List.iter (function(t) { if ctx.stack > t then write ctx Pop(ctx.stack - t); write ctx EndTrap; }) ctx.traps; write ctx (Ret (ctx.stack - ctx.limit)); ctx.stack := stack | EBreak e -> match e { | None -> () | Some e -> compile ctx false e }; var s = ctx.stack; var n = &(List.length ctx.traps - ctx.loop_traps); List.iter (function(t) { if *n > 0 then { n := *n - 1; if ctx.stack > t then write ctx Pop(ctx.stack - t); write ctx EndTrap; } }) ctx.traps; if ctx.loop_limit != ctx.stack then write ctx Pop(ctx.stack - ctx.loop_limit); ctx.stack := s; ctx.breaks := (jmp ctx , p) :: ctx.breaks | EContinue -> var s = ctx.stack; var n = &(List.length ctx.traps - ctx.loop_traps); List.iter (function(t) { if *n > 0 then { n := *n - 1; if ctx.stack > t then write ctx Pop(ctx.stack - t); write ctx EndTrap; } }) ctx.traps; if ctx.loop_limit != ctx.stack then write ctx Pop(ctx.stack - ctx.loop_limit); ctx.stack := s; ctx.continues := (jmp ctx , p) :: ctx.continues | EFunction (params,e) -> compile_function ctx params e | ENext (e1,e2) -> compile ctx false e1; compile ctx tail e2 | EObject [] -> write ctx AccNull; write ctx New | EObject fl -> var fields = List.sort compare (List.map fst fl); var id = try Hashtbl.find ctx.g.gobjects fields catch { Not_found -> var id = global ctx (GlobalVar ("o:" + Hashtbl.length ctx.g.gobjects)); Hashtbl.add ctx.g.gobjects fields id; id }; write ctx (AccGlobal id); write ctx New; write ctx Push; List.iter (function((f,e)) { write ctx Push; compile ctx false e; write ctx (SetField f); write ctx AccStack0; }) fl; write ctx (Pop 1) | ELabel l -> var l = try Hashtbl.find ctx.g.labels l catch { Not_found -> assert() }; if ctx.stack != l.lstack || List.rev(ctx.traps) != l.ltraps then error (sprintf "Label failure %d %d %s %s" (ctx.stack,l.lstack,string List.rev(ctx.traps),string l.ltraps)) p; List.iter (function(f) { f() }) l.lwait; l.lwait := []; l.lpos := Some (pos ctx) | ESwitch (e,[(econd,exec)],eo) -> compile ctx tail (EIf (EBinop "==" e econd,p) exec eo,p) | ESwitch (e,cases,eo) -> try { var ints , size = get_cases_ints cases; compile ctx false e; write ctx (JumpTable size); var tbl = Array.make size None; List.iter (function((i,e)) { tbl.[i] := Some e; }) ints; var tbl = Array.map (function(e) { (jmp ctx,e) }) tbl; Array.iter (function((j,e)) { if e == None then j() }) tbl; match eo { | None -> write ctx AccNull | Some e -> compile ctx tail e } var jump_end = jmp ctx; var tbl = Array.map (function((j,e)) { match e { | Some e -> j(); compile ctx tail e; jmp ctx | None -> function() { } } }) tbl; jump_end(); Array.iter (function(j) { j() }) tbl } catch { Exit -> compile ctx false e; write ctx Push; var jumps = List.map (function((e1,e2)) { write ctx AccStack0; write ctx Push; compile ctx false e1; write ctx Eq; (cjmp true ctx , e2) }) cases; match eo { | None -> write ctx AccNull | Some e -> compile ctx tail (EBlock [e],p) } var jump_end = jmp ctx; var jumps = List.map (function((j,e)) { j(); compile ctx tail (EBlock [e],p); jmp ctx; }) jumps; jump_end(); List.iter (function(j) { j() }) jumps; write ctx (Pop 1) } } } function compile(version,ast) { var g = { globals = Hashtbl.create(); gobjects = Hashtbl.create(); gtable = Array.create(); functions = []; labels = Hashtbl.create(); hfiles = Hashtbl.create(); files = Array.create(); }; var ctx = { g = g; version = version; stack = 0; loop_limit = 0; loop_traps = 0; limit = -1; locals = Map.empty(); ops = Array.create(); breaks = []; continues = []; env = Map.empty(); nenv = 0; traps = []; pos = Array.create(); curpos = (0,0); curfile = "_"; }; if version >= 2 then Array.add g.gtable (GlobalVersion version); scan_labels ctx true true ast; compile ctx false ast; check_breaks ctx; if g.functions != [] || Hashtbl.length g.gobjects != 0 then { var ctxops = ctx.ops; var ctxpos = ctx.pos; var ops = Array.create(); var pos = Array.create(); ctx.pos := pos; ctx.ops := ops; write ctx (Jump 0); List.iter (function((fops,fpos,gid,nargs)) { Array.set g.gtable gid GlobalFunction(Array.length ops,nargs); Array.append fops ops; Array.append fpos pos; }) (List.rev g.functions); Array.set ops 0 (Jump (Array.length ops)); var objects = Array.create(); Hashtbl.iter (function(fl,g) Array.add objects (fl,g)) g.gobjects; Array.sort (function((_,g1),(_,g2)) g1 - g2) objects; Array.iter (function((fl,g)) { write ctx AccNull; write ctx New; write ctx (SetGlobal g); List.iter (function(f) { write ctx (AccGlobal g); write ctx Push; write ctx (SetField f); }) fl }) objects; Array.append ctxpos pos; Array.append ctxops ops; }; Array.add g.gtable (GlobalDebug ctx.g.files ctx.pos); (g.gtable, ctx.ops) } neko-2-4-0/src/neko/Console.nml000066400000000000000000000067451464615675700163450ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ function read() { var buf = Buffer.create(); function rec loop() { var l = IO.read_line IO.stdin; var len = String.length l; if len > 0 && String.get l (len - 1) == '!' then Buffer.add buf (String.sub l 0 (len - 1)) else { Buffer.add buf l; Buffer.add_char buf '\n'; loop(); } } loop(); Buffer.string buf } function report(msg,p) { IO.printf IO.stderr "%s(%d): %s\n" (Lexer.source p,Lexer.line p,msg); } function run(version) { var ctx = Hashtbl.create(); function loop() { // read input print "> "; var code = read(); var bytecode , module = if String.length code > 6 && String.sub code 0 6 == "#load " then { // load bytecode var file = String.sub code 6 (String.length code - 6); var input = IO.read_file file true; var bytecode = Neko.Bytecode.read input; IO.close_in input; var input = IO.read_file file true; var str = IO.read_all input; IO.close_in input; (bytecode, str) } else { // parse and compile var input = IO.read_string code; var lex = Lexer.create Buffer.create(); Lexer.input lex "@console" input 1 0; var ast = Neko.Parser.parse lex; var bytecode = Neko.Compile.compile version ast; var output , str = IO.write_string(); Neko.Bytecode.write output bytecode; (bytecode , str()) } // read compiled module var m = Reflect.module_read (IO.input (IO.read_string module)); // set module environement var globals = fst bytecode; Array.iteri (function(i,g) { match g { | Neko.Bytecode.GlobalVar v -> try { var v = Hashtbl.find ctx v; Reflect.module_set_global m i v } catch { Not_found -> () } | _ -> () } }) globals; // execute try { nprint Reflect.neko_value(Reflect.module_execute m); nprint "\n"; } catch { e -> var st = Stack.exc(); Stack.dump IO.stdout st; print "Exception : "; match e { | Neko_error e -> nprint e | _ -> print e } print "\n"; } // save environment Array.iteri (function(i,g) { match g { | Neko.Bytecode.GlobalVar v -> Hashtbl.replace ctx v (Reflect.module_get_global m i) | _ -> () } }) globals; } while true { try { loop() } catch { | Neko.Lexer.Error(msg,pos) -> report Neko.Lexer.error_msg(msg) pos | Neko.Parser.Error(msg,pos) -> report Neko.Parser.error_msg(msg) pos | Neko.Compile.Error(msg,pos) -> report Neko.Compile.error_msg(msg) pos } } }neko-2-4-0/src/neko/Doc.nml000066400000000000000000000221071464615675700154360ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type doc_type { TBase : string; TAbstract : string; TCustom : string; TFun : (doc_type, doc_type); TOpt : doc_type; TStar : doc_type; TMult : doc_type list; TObj : (string , doc_type) list; TNamed : (string , doc_type); TPoly : (string, doc_type); TFunction : int; } type doc_item { Function : (string , doc_type, Xml.t option); Document : Xml.t; } type doc = doc_item list type token { Eof; Begin; End; Star; Question; Arrow; DoubleDot; POpen; PClose; BrOpen; BrClose; Comma; Field; Or; Quote; Sharp; Int : int; Doc : Xml.t; Ident : string; } type status { mutable doc : bool; buf : Buffer.t; } type error_msg { Unexpected : token; Invalid_char : char; Unknown_type : string; Unclosed_doc; Invalid_doc; } exception Error : (error_msg,Lexer.pos); function error(msg,p) { throw Error(msg,p) } function s_token(t) { match t { | Eof -> "" | Begin -> "/**" | End -> "**/" | DoubleDot -> ":" | Arrow -> "->" | Star -> "*" | POpen -> "(" | PClose -> ")" | BrOpen -> "{" | BrClose -> "}" | Comma -> "," | Field -> "=>" | Or -> "|" | Quote -> "'" | Sharp -> "#" | Question -> "?" | Int i -> string i | Ident i -> i | Doc d -> ""+Xml.to_string d+"" } } function error_msg(msg) { match msg { | Unexpected t -> "Unexpected " + s_token t | Invalid_char c -> "Invalid character " + String.escape_char c | Unknown_type s -> "Unknown type " + s | Unclosed_doc -> "Unclosed doc tag" | Invalid_doc -> "Documentation is not XHTML valid" } } function mk_tok(l,t) { (t,Lexer.curpos l) } function status() { { buf = Buffer.create(); doc = false; } } var doc_token = &Lexer.empty(); var doc_content = &Lexer.empty(); var doc_doc = &Lexer.empty(); doc_token := Lexer.build [ ("/\\*\\*" , function(l) { (Lexer.data l).doc := true; mk_tok l Begin }); ("/" , function(l) { Lexer.token l (*doc_token) }); ("[^/]+", function(l) { Lexer.token l (*doc_token) }); ] (function(l) { mk_tok l Eof }); doc_content := Lexer.build [ ("\\*\\*/", function(l) { (Lexer.data l).doc := false; mk_tok l End }); (":", function(l) { mk_tok l DoubleDot }); ("(", function(l) { mk_tok l POpen }); (")", function(l) { mk_tok l PClose }); ("{", function(l) { mk_tok l BrOpen }); ("}", function(l) { mk_tok l BrClose }); (",", function(l) { mk_tok l Comma }); ("=>", function(l) { mk_tok l Field }); ("|", function(l) { mk_tok l Or }); ("->", function(l) { mk_tok l Arrow }); ("\\*", function(l) { mk_tok l Star }); ("\\?", function(l) { mk_tok l Question }); ("'", function(l) { mk_tok l Quote }); ("#", function(l) { mk_tok l Sharp }); ("", function(l) { var p1 = Lexer.curpos l; var buf = (Lexer.data l).buf; Buffer.reset buf; try Lexer.token l (*doc_doc) catch { Exit -> error Unclosed_doc p1 }; var p2 = Lexer.curpos l; var x = try Xml.parse (Buffer.string buf) catch { _ -> error Invalid_doc p1 }; (Doc x, Lexer.punion p1 p2) }); ("[ \t\r\n]+", function(l) { Lexer.token l (*doc_content) }); ("[0-9]+", function(l) { mk_tok l Int(int (Lexer.current l)) }); ("$?[a-zA-Z_@][a-zA-Z0-9_@]*", function(l) { mk_tok l Ident(Lexer.current l) }); ] (function(l) { match Lexer.char l { | None -> mk_tok l Eof | Some c -> error Invalid_char(c) Lexer.curpos(l) } }); doc_doc := Lexer.build [ ("", function(l) { }); ("<", function(l) { Buffer.add_char (Lexer.data l).buf '<'; Lexer.token l (*doc_doc) }); ("[^<]+", function(l) { Buffer.add (Lexer.data l).buf Lexer.current(l); Lexer.token l (*doc_doc) }); ] (function(l) { throw Exit }); function rec documentation(s) { match s { | [< (Eof,_) >] -> [] | [< (Begin,_); d = document s; (End,_); l = documentation s >] -> d :: l } } function rec document(s) { match s { | [< (Ident i,_); (DoubleDot,_); t = doc_type s; d = doc_option s >] -> Function i t d | [< (Doc d,_) >] -> Document d } } function rec doc_option(s) { match s { | [< (Doc d,_) >] -> Some d | [< >] -> None } } function rec doc_type(s) { match s { | [< t = doc_type_base s >] -> doc_type_next t s } } function rec doc_type_base(s) { match s { | [< (Quote,_); (Ident i,_); >] -> TAbstract i | [< (Sharp,_); (Ident i,_); >] -> TCustom i | [< (Ident i,p) >] -> match i { | "null" | "int" | "float" | "string" | "array" | "bool" | "any" | "void" | "number" | "object" -> TBase i | "function" -> match s { | [< (DoubleDot,_); (Int n,_) >] -> TFunction n | [< >] -> TBase i } | _ -> match s { | [< (DoubleDot,_); t = doc_type_base s >] -> TNamed i t | [< >] -> error Unknown_type(i) p } } | [< (POpen,_); t = doc_type s; l = doc_type_list s; (PClose,_) >] -> TMult (t :: l) | [< (BrOpen,_); f = doc_type_fields s; (BrClose,_) >] -> TObj f } } function rec doc_type_next(t,s) { match s { | [< (Arrow,_); t2 = doc_type s >] -> doc_type_next (TFun t t2) s | [< (Star,_) >] -> doc_type_next (TStar t) s | [< (Question,_) >] -> doc_type_next (TOpt t) s | [< (Ident "array",_) >] -> doc_type_next TPoly("array",t) s | [< (Ident "list",_) >] -> doc_type_next TPoly("list",t) s | [< >] -> t } } function rec doc_type_list(s) { match s { | [< (Or,_); t = doc_type s; l = doc_type_list s >] -> t :: l | [< >] -> [] } } function rec doc_type_fields(s) { match s { | [< (Ident i,_); (Field,_); t = doc_type s >] -> (i,t) :: doc_type_fields s | [< (Comma,_) >] -> doc_type_fields s | [< >] -> [] } } function parse(lex) : doc { var last = &(Eof,Lexer.null_pos); function rec next_token() { var t = Lexer.token lex (if (Lexer.data lex).doc then (*doc_content) else (*doc_token)); last := t; t } try { documentation (stream next_token) } catch { Stream_error -> error Unexpected(fst(*last)) snd(*last) } } function rec type_write(ch,t) { match t { | TBase b -> IO.write ch b | TAbstract a -> IO.printf ch "'%s" a | TCustom s -> IO.printf ch "#%s" s | TFun (t1,t2) -> type_write_par ch t1; IO.write ch " -> "; type_write ch t2 | TOpt t -> type_write_par ch t; IO.write ch "?" | TStar t -> type_write_par ch t; IO.write ch "*" | TMult l -> IO.write ch "("; function rec loop(l) { match l { | [] -> () | [t] -> type_write_par ch t | t :: l -> type_write_par ch t; IO.write ch " | "; loop l } } loop l; IO.write ch ")" | TObj l -> IO.write ch "{ "; function rec loop(l) { match l { | [] -> () | [(n,t)] -> IO.write ch n; IO.write ch " => "; type_write ch t; | (n,t) :: l -> IO.write ch n; IO.write ch " => "; type_write ch t; IO.write ch ", "; loop l } } loop l; IO.write ch "}"; | TNamed (s,t) -> IO.printf ch "%s : " s; type_write_par ch t | TPoly (s,t) -> type_write_par ch t; IO.printf ch " %s" s | TFunction n -> IO.printf ch "function:%d" n } } function rec type_write_par(ch,t) { match t { | TFun _ -> IO.write ch "("; type_write ch t; IO.write ch ")"; | _ -> type_write ch t; } } function format_xml(s) { var s = String.concat "" (String.split s "["); var s = String.concat "" (String.split s "]"); s } function to_html(ch,doc) { List.iter (function(d) { match d { | Document x -> IO.write ch (format_xml Xml.to_string(x)); IO.write ch "\n" | Function (f,t,doc) -> IO.write ch "\n"; function rec loop(t) { match t { | TFun (p,r) -> var args , r = loop r; (p :: args, r) | _ -> ([],t) } } var args , r = loop t; match args { | [] -> IO.printf ch " %s : " f; type_write ch r; | _ -> IO.write ch " "; type_write_par ch r; IO.printf ch " %s(" f; function rec loop(l) { match l { | [] -> () | [t] -> type_write ch t | t :: l -> type_write ch t; IO.write ch ", "; loop l } } match args { | [TBase "void"] -> () | _ -> loop args } IO.write ch ")"; } IO.write ch " "; match doc { | None -> () | Some x -> IO.write ch (format_xml Xml.to_string(x)) } IO.write ch "\n\n"; } }) doc; }neko-2-4-0/src/neko/Lexer.nml000066400000000000000000000136251464615675700160150ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Neko.Ast; type error_msg { Invalid_character : char; Unterminated_string; Unclosed_comment; Unclosed_nxml; Invalid_escaped_character : int; Invalid_escape; } exception Error : (error_msg , Lexer.pos) function error_msg(msg) { match msg { | Invalid_character c when ord c > 32 && ord c < 128 -> sprintf "Invalid character '%c'" c | Invalid_character c -> sprintf "Invalid character 0x%.2X" (ord c) | Unterminated_string -> "Unterminated string" | Unclosed_comment -> "Unclosed comment" | Unclosed_nxml -> "Unclosed nxml" | Invalid_escaped_character n -> sprintf "Invalid escaped character %d" n | Invalid_escape -> "Invalid escape sequence" } } function error(l,msg) { throw Error(msg,Lexer.curpos l) } var keywords = { var h = Hashtbl.create(); List.iter (function(k) { Hashtbl.add h (s_keyword k) k }) [Var;While;Do;If;Else;Function;Return;Break;Continue;Try;Catch;Switch;Default]; h } function mk(l,t) { (t,Lexer.curpos l) } function mk_int(l) { mk l Const(Int(int (Lexer.current l))) } function mk_float(l) { mk l Const(Float(Lexer.current l)) } function mk_ident(l) { var s = Lexer.current l; mk l (try Keyword (Hashtbl.find keywords s) catch { Not_found -> Const (Ident s) }) } var ident = "[a-zA-Z_@][a-zA-Z0-9_@]*"; var binop = "[-!=\\*/<>&|^%\\+:]"; var expr = &Lexer.empty(); var estring = &Lexer.empty(); var ecomment = &Lexer.empty(); var enxml = &Lexer.empty(); function comment(l) { Lexer.token l (*ecomment) } function str(l) { Lexer.token l (*estring) }; function nxml(l) { Lexer.token l (*enxml) } exception Continue : bool; expr := Lexer.build [ (";", function(l) { mk l Semicolon }); (".", function(l) { mk l Dot }); (",", function(l) { mk l Comma }); ("{", function(l) { mk l BraceOpen }); ("}", function(l) { mk l BraceClose }); ("(", function(l) { mk l ParentOpen }); (")", function(l) { mk l ParentClose }); ("\\[", function(l) { mk l BracketOpen }); ("]", function(l) { mk l BracketClose }); ("=>", function(l) { mk l Arrow }); ("[ \r\t\n]+", function(l) { Lexer.token l (*expr) }); ("0x[0-9a-fA-F]+", mk_int); ("[0-9]+", mk_int); ("[0-9]+.[0-9]*", mk_float); (".[0-9]+", mk_float); ("$"+ident, function(l) { var s = Lexer.current l; var s = String.sub s 1 (String.length s - 1); mk l Const(Builtin s) }); ("true", function(l) { mk l (Const True) }); ("false", function(l) { mk l (Const False) }); ("null", function(l) { mk l (Const Null) }); ("this", function(l) { mk l (Const This) }); (ident , function(l) { mk_ident l }); ("\"", function(l) { var p1 = Lexer.curpos l; var buf = Lexer.data l; Buffer.reset buf; try str l catch { Exit -> throw Error(Unterminated_string,p1) }; var p2 = Lexer.curpos l; (Const String(Buffer.string buf) , Lexer.punion p1 p2) }); ("/\\*", function(l) { var p1 = Lexer.curpos l; var buf = Lexer.data l; Buffer.reset buf; try comment l catch { Exit -> throw Error(Unclosed_comment,p1) }; var p2 = Lexer.curpos l; (Comment(Buffer.string buf) , Lexer.punion p1 p2) }); ("//[^\r\n]*\n?", function(l) { var s = Lexer.current l; var len = String.length s; var n = (if String.get s (len - 1) == '\r' then 3 else 2); mk l CommentLine(String.sub s 0 (len - n)) }); (binop + binop + "?",function(l) { mk l (Binop (Lexer.current l)) }); (binop + binop + "=",function(l) { mk l (Binop (Lexer.current l)) }); (">>>",function(l) { mk l (Binop ">>>") }); (">>>=",function(l) { mk l (Binop ">>>=") }); ] (function(l) { match Lexer.char l { | None -> mk l Eof | Some c -> error l (Invalid_character c) } }); ; ecomment := Lexer.build [ ("\\*/", function(l) { }); ("\\*", function(l) { Buffer.add (Lexer.data l) (Lexer.current l); comment l }); ("[^*]+", function(l) { Buffer.add (Lexer.data l) (Lexer.current l); comment l }); ] (function(l) { throw Exit }); estring := Lexer.build [ ("\\\\\"", function(l) { Buffer.add_char (Lexer.data l) '"'; str l }); ("\\\\\\\\", function(l) { Buffer.add_char (Lexer.data l) '\\'; str l }); ("\\\\n", function(l) { Buffer.add_char (Lexer.data l) '\n'; str l }); ("\\\\t", function(l) { Buffer.add_char (Lexer.data l) '\t'; str l }); ("\\\\r", function(l) { Buffer.add_char (Lexer.data l) '\r'; str l }); ("\\\\[0-9][0-9][0-9]", function(l) { var k = int (String.sub (Lexer.current l) 1 3); if k > 255 then error l (Invalid_escaped_character k); Buffer.add_char (Lexer.data l) (chr k); str l }); ("\\\\" , function(l) { error l Invalid_escape }); ("\"" , function(l) { }); ("[^\\\\\"]+", function(l) { Buffer.add (Lexer.data l) (Lexer.current l); str l }); ] (function(l) { throw Exit }); enxml := Lexer.build [ ("", function(l) { throw Continue(false) }); ("<",function(l) { Buffer.add_char (Lexer.data l) '<'; throw Continue(true) }); ("[^<]+", function(l) { Buffer.add (Lexer.data l) (Lexer.current l); nxml l }); ] (function(l) { throw Exit }); neko-2-4-0/src/neko/Linker.nml000066400000000000000000000135361464615675700161630ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Neko.Bytecode; type context { globals : global array; opcodes : opcode array; debug : (int, int) array; debug_files : string array; loaded : (string, int option) Hashtbl.t; mutable version : int option; mutable have_debug : bool; } var neko_path : string list = neko "@List.@make($loader.path)"; function file_open(file) { function rec loop(l) { match l { | [] -> throw Error("File not found : " + file) | p :: l -> try IO.read_file (p + file) true; catch { _ -> loop l } } } loop ("" :: neko_path) } function rec do_link(ctx,module) { print (module + "\n"); var ch = file_open (module + ".n"); var globals, opcodes = Neko.Bytecode.read ch; IO.close_in ch; var debug = &None; var funcs = &[]; var version = &0; var gtbl = Array.map (function(g) { match g { | GlobalVar _ -> var k = Array.length ctx.globals; Array.add ctx.globals g; k | GlobalFunction (p,nargs) -> var k = Array.length ctx.globals; Array.add ctx.globals g; funcs := (p,nargs,k) :: *funcs; k | GlobalString _ | GlobalFloat _ -> try Array.index ctx.globals g catch { Not_found -> var k = Array.length ctx.globals; Array.add ctx.globals g; k } | GlobalDebug(files,inf) -> ctx.have_debug := true; debug := Some (files,inf); -1 | GlobalVersion v -> if ctx.version == None then { ctx.version := Some v; Array.add ctx.globals GlobalVersion(v); } version := v; -1 } }) globals; match ctx.version { | None -> ctx.version := Some (*version); | Some v2 -> if v2 != (*version) then throw Error("Linking modules with different version"); } var mid = Array.length ctx.globals; var module_fid = Array.length ctx.debug_files; Array.add ctx.debug_files module; Array.add ctx.globals (GlobalVar module); List.iter (Array.add ctx.opcodes) [AccNull; Push; AccBuiltin "new"; Call 1; SetGlobal mid]; Array.append (Array.make 8 (module_fid,0)) ctx.debug; Hashtbl.add ctx.loaded module None; var nops = Array.length opcodes; var opmap = Array.make nops (-1); var debug = match *debug { | None -> Array.make (nops*2) (module_fid,0) | Some(files,inf) -> var fmap = Array.map (function(f) { var p = Array.length ctx.debug_files; Array.add ctx.debug_files f; p }) files; Array.map (function((f,p)) { (fmap.[f],p) }) inf }; var debug_pos = &0; var jumps = &[]; function op(o) { Array.add ctx.opcodes o; Array.add ctx.debug (Array.get debug (*debug_pos)); debug_pos := *debug_pos + 1; if Neko.Bytecode.op_param o then { Array.add ctx.debug (Array.get debug (*debug_pos)); debug_pos := *debug_pos + 1; } } function jump(mkop,p,i) { var k = Array.length ctx.opcodes; op (Jump 0); jumps := (mkop,k,p,i) :: *jumps } function rec loop((p,l)) { match l { | [] -> assert() // $loader.loadmodule("name",$loader) | (AccGlobal str) :: Push :: (AccBuiltin "loader") :: Push :: (AccBuiltin "loader") :: Push :: (AccField "loadmodule") :: (ObjCall 2) :: l -> match Array.get globals str { | GlobalString s -> var mid = try Hashtbl.find ctx.loaded s catch { Not_found -> Some (do_link ctx s) }; match mid { | None -> throw Error("Recursive loading " + module + " => " + s) | Some i -> op (AccGlobal i); debug_pos := *debug_pos + 11; (p + 8, l) } | _ -> throw Error("Cannot link not constant file") } | o :: l -> Array.set opmap p (Array.length ctx.opcodes); match o { | AccBuiltin "exports" -> op (AccGlobal mid) | AccGlobal g -> op (AccGlobal (Array.get gtbl g)); | SetGlobal g -> op (SetGlobal (Array.get gtbl g)); | Jump i -> jump Jump p i | JumpIf i -> jump JumpIf p i | JumpIfNot i -> jump JumpIfNot p i | Trap i -> jump Trap p i | _ -> op o } (p+1 , l) } } var l = &(0,Array.list opcodes); while snd (*l) != [] { l := loop (*l) } List.iter (function((op,k,p,i)) { var ik = Array.get opmap (p + i) - Array.get opmap p; Array.set ctx.opcodes k op(ik) }) (*jumps); List.iter (function((p,nargs,k)) { Array.set ctx.globals k (GlobalFunction (Array.get opmap p) nargs) }) (*funcs); Hashtbl.replace ctx.loaded module (Some mid); Array.add ctx.opcodes (AccGlobal mid); Array.add ctx.debug (module_fid,0); Array.add ctx.debug (module_fid,0); mid } function link(output,modules) { var ctx = { globals = Array.create(); opcodes = Array.create(); debug = Array.create(); debug_files = Array.create(); loaded = Hashtbl.create(); have_debug = false; version = None; }; List.iter (function(m) { ignore(do_link ctx m) }) modules; if ctx.have_debug then Array.add ctx.globals (GlobalDebug ctx.debug_files ctx.debug); var ch = IO.write_file output true; Neko.Bytecode.write ch (ctx.globals,ctx.opcodes); IO.close_out ch } neko-2-4-0/src/neko/Main.nml000066400000000000000000000130351464615675700156150ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ var dir = &None; var verbose = &false; function out(file,ext) { var file = Sys.without_extension file + ext; match *dir { | None -> file | Some dir -> dir + Sys.without_dir file } } function is_nxml(str) { var len = String.length str; if len <= 10 || String.sub str 0 6 != "" then None else if String.sub str (len - 7) 7 == "" then Some (String.sub str 6 (len - 13)) else if String.sub str (len - 8) 8 == "\n" then Some (String.sub str 6 (len - 14)) else if String.sub str (len - 9) 9 == "\r\n" then Some (String.sub str 6 (len - 15)) else None } function parse_multiformat(file) { var str = IO.file_contents file; match is_nxml str { | Some nxml -> Neko.Xml.parse nxml Lexer.null_pos | None when String.length str > 4 && String.sub str 0 4 == Neko.Binast.header -> Neko.Binast.parse (IO.read_string str) Lexer.null_pos | None -> var i = IO.read_string str; var lex = Lexer.create Buffer.create(); Lexer.input lex file i 1 0; Neko.Parser.parse lex; } } function compile(version,file) { if *verbose then printf "Compiling %s\n" file; var ast = parse_multiformat file; var code = Neko.Compile.compile version ast; var o = IO.write_file out(file,".n") true; Neko.Bytecode.write o code; IO.close_out o } function dump(file) { if *verbose then printf "Dumping %s\n" file; var i = IO.read_file file true; var code = Neko.Bytecode.read i; IO.close_in i; var o = IO.write_file out(file,".dump") false; Neko.Bytecode.dump o code; IO.close_out o } function print_ast(file) { if *verbose then printf "Printing %s\n" file; var ast = parse_multiformat file; var o = IO.write_file out(file,"2.neko") false; Neko.Printer.print Neko.Printer.create(o) ast; IO.close_out o } function release(file) { if *verbose then printf "Releasing %s\n" file; var i = IO.read_file file true; var globals, code = Neko.Bytecode.read i; IO.close_in i; var globals = Array.map (function(g) { match g { | Neko.Bytecode.GlobalVar _ | Neko.Bytecode.GlobalDebug _ -> Neko.Bytecode.GlobalVar "" | _ -> g } }) globals; var o = IO.write_file out(file,".n") true; Neko.Bytecode.write o (globals,code); IO.close_out o } function documentation(file) { if *verbose then printf "Building documentation for %s\n" file; var i = IO.read_file file false; var lex = Lexer.create Neko.Doc.status(); Lexer.input lex file i 1 0; var doc = Neko.Doc.parse lex; IO.close_in i; var o = IO.write_file out(file,".html") false; Neko.Doc.to_html o doc; IO.close_out o } function report(msg,p) { if p == Lexer.null_pos then { Stack.dump IO.stderr Stack.exc(); IO.printf IO.stderr "Exception : %s\n" msg } else IO.printf IO.stderr "%s(%d): %s\n" (Lexer.source p,Lexer.line p,msg); Sys.exit(-1); } function complete(dir) { var l = String.length dir; if l > 0 && String.get dir (l - 1) != '/' && String.get dir (l - 1) != '\\' then dir + "/" else dir } try { var v = Sys.version; var year : int = neko "$neko_build_year()"; var head = "Neko Compiler v" + v.maj + "." + v.min + "." + v.build + " - (c)2005-" + year + " Haxe Foundation\n Usage : nekoc [options] files..."; var link = &None; var links = &[]; var version = &0; var decl = [ ("-d", Args.String (function(f) { dump f }) , " : dump bytecode"); ("-z", Args.String (function(f) { release f }), " : make bytecode release"); ("-p", Args.String (function(f) { print_ast f }), " : parse and print neko source"); ("-doc", Args.String (function(f) { documentation f }) , " : make documentation"); ("-o", Args.String (function(f) { dir := Some (complete f) })," : set output directory"); ("-console", Args.Void (function() { Neko.Console.run(*version) }),": run the console"); ("-link", Args.String (function(f) { link := Some f })," : link bytecodes files"); ("-v", Args.Void (function() { verbose := true }) , ": verbose mode"); ("-version", Args.Int (function(v) version := v), ": set the bytecode version"); ]; Args.parse head decl (function(f) { match *link { | None -> compile (*version) f | Some _ -> links := f :: *links } }); match *link { None -> () | Some f -> Neko.Linker.link f List.rev(*links) }; } catch { | Neko.Lexer.Error(msg,pos) -> report Neko.Lexer.error_msg(msg) pos | Neko.Parser.Error(msg,pos) -> report Neko.Parser.error_msg(msg) pos | Neko.Compile.Error(msg,pos) -> report Neko.Compile.error_msg(msg) pos | Neko.Doc.Error(msg,pos) -> report Neko.Doc.error_msg(msg) pos | e -> report string(e) Lexer.null_pos } neko-2-4-0/src/neko/Parser.nml000066400000000000000000000166161464615675700161750ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Lexer; open Neko.Ast; type error_msg { Unexpected : token; Unclosed : string; Invalid_nxml : error; } exception Error : (error_msg , pos) function error_msg(m) { match m { | Unexpected t -> "Unexpected " + s_token t | Unclosed s -> "Unclosed " + s | Invalid_nxml e -> "Invalid nxml (" + string e + ")" } } function error(m,p) { throw Error(m,p) } function priority(x) { match x { | "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "<<=" | ">>=" | ">>>=" | "|=" | "&=" | "^=" -> -4 | "++=" | "--=" -> -3 | "&&" | "||" -> -2 | "==" | "!=" | ">" | "<" | "<=" | ">=" -> -1 | "+" | "-" -> 0 | "*" | "/" -> 1 | "|" | "&" | "^" -> 2 | "<<" | ">>" | "%" | ">>>" -> 3 | _ -> 4 } } function mk(t,p) { (t,p) } function rec make_binop(op,e,e2) { var v , p2 = e2; match v { | EBinop (_op,_e,_e2) when priority _op <= priority op -> var _e = make_binop op e _e; mk EBinop(_op,_e,_e2) punion(pos _e,pos _e2) | _ -> mk EBinop(op,e,e2) punion(pos e,pos e2) } } function rec program(s) { match s { | [< e = expr s; p = program s >] -> e :: p | [< (Semicolon,_); p = program s >] -> p | [< (Eof,_) >] -> [] } } function rec expr(s) { match s { | [< (Const ((Ident k) as i),p) >] -> match s { | [< (Binop ":",p2) >] -> mk ELabel(k) punion(p,p2) | [< >] -> expr_next (EConst i,p) s } | [< (Const c,p) >] -> expr_next (EConst c,p) s | [< (BraceOpen,p1); e = block1 s >] -> match s { | [< (BraceClose,p2) >] -> expr_next (e,punion p1 p2) s | [< (Eof,_) >] -> error (Unclosed "{") p1 } | [< (ParentOpen,p1); e = expr s >] -> match s { | [< (ParentClose,p2) >] -> expr_next (EParenthesis e,punion p1 p2) s | [< (Eof,_) >] -> error (Unclosed "(") p1 } | [< (Keyword Var,p1); v , p2 = variables p1 s >] -> expr_next (EVars v,punion p1 p2) s | [< (Keyword While,p1); cond = expr s; e = expr s >] -> expr_next (EWhile cond e NormalWhile, punion p1 (pos e)) s | [< (Keyword Do,p1); e = expr s; (Keyword While,_); cond = expr s >] -> expr_next (EWhile cond e DoWhile, punion p1 (pos cond)) s | [< (Keyword Switch,p1); v = expr s; (BraceOpen,_); l , def = switch_cases s; (BraceClose,p2) >] -> expr_next (ESwitch v l def,punion p1 p2) s; | [< (Keyword If,p1); cond = expr s; e = expr s >] -> function rec loop() { match s { | [< (Keyword Else,_); e2 = expr s >] -> expr_next (EIf cond e Some(e2),punion p1 (pos e2)) s | [< (Semicolon,_) >] -> loop() | [< >] -> expr_next (EIf cond e None,punion p1 (pos e)) s } }; loop() | [< (Keyword Function,p1); (ParentOpen,po); p = parameter_names s >] -> match s { | [< (ParentClose,_); e = expr s >] -> expr_next (EFunction p e,punion p1 (pos e)) s | [< (Eof,_) >] -> error (Unclosed "(") po } | [< (Keyword Return,p1) >] -> match s { | [< e = expr s >] -> expr_next (EReturn (Some e), punion p1 (pos e)) s | [< (Semicolon,_) >] -> expr_next (EReturn None,p1) s } | [< (Keyword Break,p1) >] -> match s { | [< e = expr s >] -> expr_next (EBreak (Some e), punion p1 (pos e)) s | [< (Semicolon,_) >] -> expr_next (EBreak None,p1) s } | [< (Keyword Continue,p1) >] -> expr_next (EContinue,p1) s | [< (Keyword Try,p1); e = expr s; (Keyword Catch,_); (Const (Ident name),_); e2 = expr s >] -> expr_next (ETry e name e2,punion p1 (pos e2)) s | [< (Binop "-",p1); e2 = expr s >] -> (EParenthesis (make_binop "-" (EConst (Int 0),p1) e2), punion p1 (pos e2)) } } function rec expr_next(e,s) { match s { | [< (Dot,_); (Const (Ident name),p) >] -> expr_next (EField e name,punion (pos e) p) s | [< (ParentOpen,po); pl = parameters s >] -> match s { | [< (ParentClose,p) >] -> expr_next (ECall e pl,punion (pos e) p) s | [< (Eof,_) >] -> error (Unclosed "(") po } | [< (BracketOpen,po); e2 = expr s >] -> match s { | [< (BracketClose,p) >] -> expr_next (EArray e e2,punion (pos e) p) s | [< (Eof,_) >] -> error (Unclosed "[") po } | [< (Binop op,_); e2 = expr s >] -> make_binop op e e2 | [< >] -> e } } function rec block1(s) { match s { | [< (Const (Ident name),p) >] -> match s { | [< (Arrow,_); e = expr s; l = object_fields s >] -> EObject ((name,e) :: l) | [< (Binop ":",p2); b = block s >] -> EBlock ( (ELabel name, punion p p2) :: b ) | [< e = expr_next (EConst (Ident name),p) s; b = block s >] -> EBlock (e :: b) } | [< b = block s >] -> EBlock b } } function rec block(s) { match s { | [< e = expr s; b = block s >] -> e :: b | [< (Semicolon,_); b = block s >] -> b | [< >] -> [] } } function rec object_fields(s) { match s { | [< (Const (Ident name),_); (Arrow,_); e = expr s; l = object_fields s >] -> (name,e) :: l | [< (Comma,_); l = object_fields s >] -> l | [< >] -> [] } } function rec switch_cases(s) { match s { | [< e1 = expr s; (Arrow,p); e2 = expr s; l , def = switch_cases s >] -> ((e1,e2) :: l , def) | [< (Keyword Default,pp); (Arrow,p); e = expr s; l, def = switch_cases s >] -> match def { | None -> (l, Some e) | Some _ -> error (Unexpected (Keyword Default)) pp } | [< >] -> ([] , None) } } function rec parameter_names(s) { match s { | [< (Const (Ident name),_); p = parameter_names s >] -> name :: p | [< (Comma,_); p = parameter_names s >] -> p | [< >] -> [] } } function rec parameters(s) { match s { | [< e = expr s; p = parameters_next s >] -> e :: p | [< >] -> [] } } function rec parameters_next(s) { match s { | [< (Comma,_); p = parameters s >] -> p | [< >] -> [] } } function rec variables(sp,s) { match s { | [< (Const (Ident name),p) >] -> match s { | [< (Binop "=",_); e = expr s; v , p = variables_next (pos e) s >] -> ((name, Some e) :: v , p) | [< v , p = variables_next p s >] -> ((name, None) :: v , p) } } } function rec variables_next(sp,s) { match s { | [< (Comma,p); v = variables p s >] -> v | [< >] -> ([] , sp) } } function parse(lexer) { var last = &(Eof,null_pos); function rec next_token() { var t = Lexer.token lexer (*Neko.Lexer.expr); match fst t { | Comment s | CommentLine s -> next_token() | _ -> last := t; t } } try { var p = program (stream next_token); (EBlock p,null_pos) } catch { Stream_error -> error Unexpected(fst(*last)) snd(*last) } } function parse_string(str,p) { var ch = IO.read_string str; var lex = Lexer.create Buffer.create(); Lexer.input lex p.psource ch p.pline p.pmin; parse lex } Neko.Xml.parse_string := parse_string; Neko.Binast.parse_from_string := parse_string; neko-2-4-0/src/neko/Printer.nml000066400000000000000000000127561464615675700163650ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Neko.Ast type ctx { ch : IO.output; mutable level : int; mutable tabs : bool; } function create(ch) { { ch = ch; level = 0; tabs = true; } } function newline(ctx) { IO.write_char ctx.ch '\n'; ctx.tabs := false; } function level(ctx,b) { ctx.level := ctx.level + (if b then 1 else -1); newline ctx } function printf(ctx) { if !ctx.tabs then { IO.write ctx.ch (String.make (ctx.level * 4) ' '); ctx.tabs := true; }; IO.printf ctx.ch } function rec print_list(ctx,sep,f,x) { match x { | [] -> () | x :: [] -> f x | x :: l -> f x; printf ctx "%s" sep; print_list ctx sep f l } } function print(ctx,n) { printf ctx "%s" n } function rec print_ast(ctx,binop,(e,p)) { var print_rec = print_ast ctx false; match e { | EConst c -> printf ctx "%s" (s_constant c) | EBlock el -> print ctx "{"; level ctx true; List.iter (function(e) { print_rec e; if ctx.tabs then { print ctx ";"; newline ctx; } }) el; ctx.level := ctx.level - 1; print ctx "}"; newline ctx; | EParenthesis e when !ctx.tabs -> print ctx "{ "; print_rec e; print ctx " }"; | EParenthesis e -> print ctx "( "; print_rec e; print ctx " )"; | EField (e,s) -> print_rec e; printf ctx ".%s" s; | ECall (e,el) -> print_rec e; print ctx "("; print_list ctx "," print_rec el; print ctx ")"; | EArray (e1,e2) -> print_rec e1; print ctx "["; print_rec e2; print ctx "]" | EVars vl -> print ctx "var "; print_list ctx ", " (function((n,v)) { printf ctx "%s" n; match v { | None -> () | Some e -> print ctx " = "; print_rec e } }) vl; print ctx ";"; newline ctx | EWhile(cond,e,NormalWhile) -> print ctx "while "; print_rec cond; level_expr ctx e false; | EWhile (cond,e,DoWhile) -> print ctx "do "; level_expr ctx e false; print ctx "while "; print_rec cond; newline ctx | EIf (cond,e,e2) -> print ctx "if "; print_rec cond; level_expr ctx e (e2 == None); match e2 { | None -> () | Some e -> print ctx "else"; level_expr ctx e false } | ETry (e,id,e2) -> print ctx "try"; level_expr ctx e false; printf ctx "catch %s" id; level_expr ctx e2 false; | EFunction (params,e) -> print ctx "function("; print_list ctx "," (printf ctx "%s") params; print ctx ")"; level_expr ctx e false; | EBinop (op,e1,e2) -> var tabs = ctx.tabs; if binop then (if tabs then print ctx "(" else print ctx "{"); print_ast ctx true e1; printf ctx " %s " op; print_ast ctx true e2; if binop then (if tabs then print ctx ")" else print ctx "}"); | EReturn None -> print ctx "return;"; | EReturn (Some e) -> print ctx "return "; print_rec e; | EBreak None -> print ctx "break;"; | EBreak (Some e) -> print ctx "break "; print_rec e; | EContinue -> print ctx "continue" | ENext (e1,e2) -> print_rec e1; print ctx ";"; newline ctx; print_rec e2 | EObject [] -> print ctx "$new(null)" | EObject fl -> print ctx "{"; level ctx true; function rec loop(x) { match x { | [] -> assert() | [(f,e)] -> printf ctx "%s => " f; print_rec e; newline ctx; | (f,e) :: l -> printf ctx "%s => " f; print_rec e; print ctx ", "; newline ctx; loop l } }; loop fl; level ctx false; print ctx "}" | ELabel s -> printf ctx "%s:" s | ESwitch (e,cases,eo) -> print ctx "switch "; print_rec e; print ctx "{"; newline ctx; List.iter (function((e1,e2)) { print_rec e1; print ctx " => "; level ctx true; print_rec e2; level ctx false; }) cases; match eo { | None -> () | Some e -> print ctx "default => "; level ctx true; print_rec e; level ctx false } print ctx "}"; newline ctx } } function rec level_expr(ctx,(e,p),closed) { match e { | EBlock _ -> if ctx.tabs then print ctx " "; print_ast ctx false (e,p) | EParenthesis e -> if ctx.tabs then print ctx " "; print ctx "{"; level ctx true; print_ast ctx false e; level ctx false; print ctx "}"; | _ -> level ctx true; print_ast ctx false (e,p); if closed then print ctx ";"; level ctx false } } function print(ctx,ast) { match fst ast { | EBlock el -> List.iter (function(e) { print_ast ctx false e; if ctx.tabs then { print ctx ";"; newline ctx; } }) el; | _ -> print_ast ctx false ast } } function to_string(ast) { var ch , str = IO.write_string(); var ctx = create ch; print ctx ast; IO.close_out ch; str() } neko-2-4-0/src/neko/Xml.nml000066400000000000000000000165401464615675700154750ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Lexer; open Neko.Ast; open Xml; exception Error : string; var parse_string : (string -> pos -> expr) ref = &(function(_,_) { assert() }); function parse_pos(p,x) { try { var a = attrib x "p"; var f, l = match String.split a ":" { | [] -> assert() | [line] -> (p.psource, int line) | l -> match List.rev l { | [] -> assert() | line :: l -> (String.concat ":" (List.rev l),int line) } }; { psource = f; pmin = p.pmin; pmax = p.pmax; pline = l; } } catch { Not_found -> p } } function rec parse_xml(p,x) { function error() { invalid_arg(); } var p = parse_pos p x; function binop(f) { match nodes x { | [a;b] -> (f (parse_xml p a) (parse_xml p b),p) | _ -> error() } } match node_name x { | "i" -> var v = int (attrib x "v"); (EConst Int(v),p) | "f" -> var v = attrib x "v"; (EConst Float(v),p) | "s" -> var v = attrib x "v"; (EConst String(String.unescape v),p) | "v" -> var v = attrib x "v"; if String.get v 0 == '$' then (EConst Builtin(String.sub v 1 (String.length v - 1)),p) else match v { | "true" -> (EConst True,p) | "false" -> (EConst False,p) | "null" -> (EConst Null,p) | "this" -> (EConst This,p) | _ -> (EConst Ident(v),p) } | "b" -> (EBlock (List.map (parse_xml p) (nodes x)),p) | "p" -> (EParenthesis (parse_xml p (firstNode x)),p) | "g" -> var v = attrib x "v"; (EField (parse_xml p (firstNode x)) v,p) | "c" -> match nodes x { | [] -> error() | x :: l -> (ECall (parse_xml p x) (List.map (parse_xml p) l),p) } | "a" -> binop EArray | "var" -> (EVars (List.map (function(x) { var e = try Some (firstNode x) catch { _ -> None }; (attrib x "v", match e { None -> None | Some x -> Some (parse_xml p x) }) }) nodes(x)),p) | "while" -> binop (function(a,b) { EWhile a b NormalWhile }) | "do" -> binop (function(a,b) { EWhile b a DoWhile }) | "if" -> match nodes x { | [cond;e] -> (EIf (parse_xml p cond) (parse_xml p e) None,p) | [cond;e1;e2] -> (EIf (parse_xml p cond) (parse_xml p e1) Some(parse_xml p e2),p) | _ -> error() } | "o" -> var v = attrib x "v"; binop (EBinop v) | "try" -> var v = attrib x "v"; binop (function(a,b) { ETry a v b }) | "function" -> var args = String.split (attrib x "v") ":"; (EFunction args (parse_xml p (firstNode x)),p) | "return" -> var x = try Some (firstNode x) catch { _ -> None }; (EReturn (match x { None -> None | Some x -> Some (parse_xml p x) }),p) | "break" -> var x = try Some (firstNode x) catch { _ -> None }; (EBreak (match x { None -> None | Some x -> Some (parse_xml p x) }),p) | "continue" -> (EContinue,p) | "next" -> binop ENext | "object" -> (EObject (List.map (function(x) { (attrib x "v", parse_xml p (firstNode x)) }) nodes(x)),p) | "label" -> var v = attrib x "v"; (ELabel v,p) | "switch" -> var cases = &[]; var def = &None; var e = &None; List.iter (function(x) { match node_name x { | "case" -> var c = match nodes x { | [a;b] -> (parse_xml p a, parse_xml p b) | _ -> error() }; cases := c :: *cases; | "default" -> if *def != None then error(); def := Some (parse_xml p firstNode(x)); | _ -> if *e != None then error(); e := Some (parse_xml p x); } }) nodes(x); match *e { | None -> error() | Some e -> (ESwitch e (List.rev (*cases)) (*def),p) } | "neko" -> var e , _ = (*parse_string) (node_text x) p; (e,p) | n -> throw Error("Unknown node name : "+n); } } function parse(s,p) { var x = parse s; match nodes x { | [(Node _) as x] -> parse_xml p x | _ -> invalid_arg() } } function rec to_xml_rec(p2,ast) { var e , p = ast; var name = &""; var val = &None; var children = &[]; match e { | EConst c -> match c { | True | False | Null | This | Builtin _ | Ident _ -> name := "v"; val := Some (s_constant c) | Int i -> name := "i"; val := Some (string i); | Float s -> name := "f"; val := Some s; | String s -> name := "s"; val := Some s; } | EBlock el -> name := "b"; children := List.map (to_xml_rec p) el; | EParenthesis e -> name := "p"; children := [to_xml_rec p e]; | EField (e,f) -> name := "g"; val := Some f; children := [to_xml_rec p e]; | ECall (e,el) -> name := "c"; children := to_xml_rec p e :: List.map (to_xml_rec p) el; | EArray (a,b) -> name := "a"; children := [to_xml_rec p a; to_xml_rec p b]; | EVars vl -> name := "var"; children := List.map (function((v,e)) { Node "v" [("v",v)] (match e { None -> [] | Some e -> [to_xml_rec p e] }) }) vl; | EWhile (econd,e,NormalWhile) -> name := "while"; children := [to_xml_rec p econd; to_xml_rec p e]; | EWhile (econd,e,DoWhile) -> name := "do"; children := [to_xml_rec p e; to_xml_rec p econd]; | EIf (cond,e,eelse) -> name := "if"; children := to_xml_rec p cond :: to_xml_rec p e :: (match eelse { None -> [] | Some e -> [to_xml_rec p e] }) | ETry (e1,v,e2) -> name := "try"; val := Some v; children := [to_xml_rec p e1; to_xml_rec p e2]; | EFunction (args,e) -> name := "function"; val := Some (String.concat ":" args); children := [to_xml_rec p e]; | EBinop (op,e1,e2) -> name := "o"; val := Some op; children := [to_xml_rec p e1; to_xml_rec p e2]; | EReturn e -> name := "return"; children := match e { None -> [] | Some e -> [to_xml_rec p e]}; | EBreak e -> name := "break"; children := match e { None -> [] | Some e -> [to_xml_rec p e]}; | EContinue -> name := "continue"; | ENext (e1,e2) -> name := "next"; children := [to_xml_rec p e1; to_xml_rec p e2]; | EObject fl -> name := "object"; children := List.map (function((v,e)) { Node "v" [("v",v)] [to_xml_rec p e] }) fl; | ELabel v -> name := "label"; val := Some v; | ESwitch (e,cases,def) -> name := "switch"; var cases = List.map (function((e1,e2)) { Node "case" [] [to_xml_rec p e1; to_xml_rec p e2] }) cases; children := to_xml_rec p e :: (match def { None -> cases | Some e -> Node "default" [] [to_xml_rec p e] :: cases }); } var pos = (if p.psource != p2.psource then [("p",p.psource + ":" + p.pline)] else if p.pline != p2.pline then [("p",string p.pline)] else []); var val = match *val { None -> [] | Some v -> [("v",v)] }; Node (*name) (List.append pos val) (*children) } function to_xml(ast) { to_xml_rec null_pos ast } neko-2-4-0/src/nekoml/000077500000000000000000000000001464615675700145505ustar00rootroot00000000000000neko-2-4-0/src/nekoml/Ast.nml000066400000000000000000000106221464615675700160100ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type pos = Lexer.pos type constant { Int : int; Char : char; Bool : bool; Float : string; String : string; Ident : string; Constr : string; Module : (string list , constant); } type keyword { Var; If; Else; Function; Try; Catch; Type; Match; Then; When; While; Exception; } type token { Eof; Semicolon; Dot; Comma; Quote; BraceOpen; BraceClose; ParentOpen : bool; ParentClose; BracketOpen; BracketClose; Arrow; Vertical; StreamOpen; StreamClose; Const : constant; Keyword : keyword; Binop : string; Comment : string; CommentLine : string; } type type_path { EType : (type_path option , string list , string); EPoly : string; ETuple : type_path list; EArrow : (type_path , type_path); } type type_decl { EAbstract; EAlias : type_path; ERecord : (string , bool , type_path) list; EUnion : (string , type_path option) list; } type arg { ATyped : (arg , type_path); ANamed : string; ATuple : arg list; } type pattern; type expr; type stream_item; type pattern_decl { PIdent : string; PConst : constant; PTuple : pattern list; PRecord : ((string , pattern) list, int); PConstr : (string list , string , pattern option); PAlias : (string , pattern); PTyped : (pattern , type_path); PStream : (stream_item list , int); } type stream_item { SPattern : pattern; SExpr : (string list , expr); SMagicExpr : (pattern , int); } type expr_decl { EConst : constant; EBlock : expr list; EField : (expr , string); ECall : (expr , expr list); EArray : (expr , expr); EVar : ((string , type_path option) list , expr); EIf : (expr , expr , expr option); EFunction : (bool , string option , arg list , expr , type_path option); EBinop : (string , expr , expr); EUnop : (string , expr); ETypeAnnot : (expr , type_path); ETupleDecl : expr list; ETypeDecl : (string list , string , type_decl); EErrorDecl : (string , type_path option); ERecordDecl : (string , expr) list; EMatch : (expr , (pattern list , expr option , expr) list); ETry : (expr , (pattern list , expr option , expr) list); ETupleGet : (expr , int); EApply : (expr , expr list); EWhile : (expr , expr); } type pattern = (pattern_decl , pos) type expr = (expr_decl , pos) var pos = snd; function rec s_constant(c) { match c { | Int i -> string i | Float s -> s | Bool b -> if b then "true" else "false" | Char c -> "\"" + String.escape_char c + "\"" | String s -> "\"" + String.escape s + "\"" | Ident s -> s | Constr s -> s | Module (l,c) -> String.concat "." l + "." + s_constant c } } function s_path(path,n) { match path { | [] -> n | _ -> String.concat "." path + "." + n } } function s_keyword(k) { match k { | Var -> "var" | If -> "if" | Else -> "else" | Function -> "function" | Try -> "try" | Catch -> "catch" | Type -> "type" | Match -> "match" | Then -> "then" | When -> "when" | While -> "while" | Exception -> "exception" } } function s_token(t) { match t { | Eof -> "" | Semicolon -> ";" | Dot -> "." | Comma -> "," | Quote -> "'" | BraceOpen -> "{" | BraceClose -> "}" | ParentOpen _ -> "(" | ParentClose -> ")" | BracketOpen -> "[" | BracketClose -> "]" | StreamOpen -> "[<" | StreamClose -> ">]" | Arrow -> "->" | Vertical -> "|" | Const c -> s_constant c | Keyword k -> s_keyword k | Binop s -> s | Comment s -> "/*" + s + "*--" | CommentLine s -> "//" + s } } neko-2-4-0/src/nekoml/Lexer.nml000066400000000000000000000121051464615675700163360ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Nekoml.Ast type error_msg { Invalid_character : char; Unterminated_string; Unclosed_comment; Invalid_escaped_character : int; Invalid_escape; } exception Error : (error_msg , pos) function error_msg(msg) { match msg { | Invalid_character c -> sprintf "Invalid character '%s'" (String.escape_char c) | Unterminated_string -> "Unterminated string" | Unclosed_comment -> "Unclosed comment" | Invalid_escaped_character n -> sprintf "Invalid escaped character %d" n | Invalid_escape -> "Invalid escape sequence" } } function error(l,msg) { throw Error(msg,Lexer.curpos l) } var keywords = { var h = Hashtbl.create(); List.iter (function(k) { Hashtbl.add h (s_keyword k) k }) [Var;If;Else;Function;Try;Catch;Type;Match;Then;When;While;Exception]; h } function mk(l,t) { (t,Lexer.curpos l) } function mk_ident(l) { var s = Lexer.current l; mk l (try Keyword (Hashtbl.find keywords s) catch { Not_found -> Const (Ident s) }) } function mk_int(l) { mk l (Const (Int (int (Lexer.current l)))) } function mk_float(l) { mk l (Const (Float (Lexer.current l))) } function mk_binop(l) { mk l (Binop (Lexer.current l)); } var ident = "[a-z_][a-zA-Z0-9_]*"; var modident = "[A-Z][a-zA-Z0-9_]*"; var binop = "[-!=*/<>&|%+:]"; var number = "[0-9]"; var numbers = number + "+"; var spaces = "[ \r\n\t]+"; var token = &Lexer.empty(); function comment(l) { Lexer.token l (*Neko.Lexer.ecomment) } function str(l) { Lexer.token l (*Neko.Lexer.estring) } token := Lexer.build [ (";", function(l) { mk l Semicolon }); (".", function(l) { mk l Dot }); (",", function(l) { mk l Comma }); ("{", function(l) { mk l BraceOpen }); ("}", function(l) { mk l BraceClose }); (spaces + "(", function(l) { mk l (ParentOpen true) }); ("(", function(l) { mk l (ParentOpen false) }); (")", function(l) { mk l ParentClose }); ("\\[", function(l) { mk l BracketOpen }); ("]", function(l) { mk l BracketClose }); ("'", function(l) { mk l Quote }); ("|", function(l) { mk l Vertical }); (spaces, function(l) { Lexer.token l (*token) }); ( "0x[0-9a-fA-F]+", mk_int ); ( numbers, mk_int ); ( numbers + "." + number + "*", mk_float); ( "." + numbers, mk_float ); ("true", function(l) { mk l (Const (Bool true)) }); ("false", function(l) { mk l (Const (Bool false))}); ("\"", function(l) { var p1 = Lexer.curpos l; var buf = Lexer.data l; Buffer.reset buf; try str l catch { Exit -> throw Error(Unterminated_string,p1) }; var p2 = Lexer.curpos l; (Const String(Buffer.string buf) , Lexer.punion p1 p2) }); ("/\\*", function(l) { var p1 = Lexer.curpos l; var buf = Lexer.data l; Buffer.reset buf; try comment l catch { Exit -> throw Error(Unclosed_comment,p1) }; var p2 = Lexer.curpos l; (Comment(Buffer.string buf) , Lexer.punion p1 p2) }); ("'\\\\n'", function(l) { mk l Const(Char '\n') }); ("'\\\\t'", function(l) { mk l Const(Char '\t') }); ("'\\\\r'", function(l) { mk l Const(Char '\r') }); ("'\\\\''", function(l) { mk l Const(Char '\'') }); ("'\\\\\\\\'", function(l) { mk l Const(Char '\\') }); ("'\\\\\"'", function(l) { mk l Const(Char '"') }); ("'[^\\\\]'", function(l) { mk l Const(Char (String.get Lexer.current(l) 1)) }); ("'\\\\[0-9][0-9][0-9]'", function(l) { var s = String.sub (Lexer.current l) 2 3; var n = int s; if n > 255 then error l Invalid_escaped_character(n); mk l (Const (Char (chr n))) }); ("//[^\r\n]*\n?", function(l) { var s = Lexer.current l; var len = String.length s; var n = (if String.get s (len - 1) == '\r' then 3 else 2); mk l CommentLine(String.sub s 0 (len - n)) }); ("\\[<", function(l) { mk l StreamOpen }); (">]", function(l) { mk l StreamClose }); ("->", function(l) { mk l Arrow }); (binop + binop + "?", mk_binop); (">>>" , mk_binop); ("===" , mk_binop); ("!==" , mk_binop); ("or" , mk_binop); ("and" , mk_binop); ("xor" , mk_binop); (ident , mk_ident); (modident , function(l) { mk l Const(Constr (Lexer.current l)) }); ] (function(l) { match Lexer.char l { | None -> mk l Eof | Some c -> error l (Invalid_character c) } }); neko-2-4-0/src/nekoml/Main.nml000066400000000000000000000125001464615675700161420ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Nekoml.Typer; function complete(s) { var l = String.length s; if l > 0 && String.get s (l - 1) != '\\' && String.get s (l-1) != '/' then s + "/" else s } function report(msg,p) { if p == Lexer.null_pos then { Stack.dump IO.stderr Stack.exc(); IO.printf IO.stderr "Exception : %s\n" msg } else IO.printf IO.stderr "%s(%d): %s\n" (Lexer.source p,Lexer.line p,msg); Sys.exit(-1); } function gen_neko(ctx,m,e) { var path , deps, idents = module_infos m; var file = Nekoml.Type.file_name path ".neko"; if *verbose then printf "Generating %s\n" file; var e = Nekoml.Neko.generate ctx e deps idents path; var ch = IO.write_file file false; var ctx = Neko.Printer.create ch; Neko.Printer.print ctx e; IO.close_out ch } function compile(ctx,m,e) { var path, deps, idents = module_infos m; var file = Nekoml.Type.file_name path ".n"; if *verbose then printf "Compiling %s\n" file; /* don't use version 1+ since this will use left-to-right evaluation for $array, leading to too much stack used with big chained lists */ var code = Neko.Compile.compile 0 (Nekoml.Neko.generate ctx e deps idents path); var ch = IO.write_file file true; Neko.Bytecode.write ch code; IO.close_out ch } function capitalize(p) { var c = String.get p 0; if c < 'a' || c > 'z' then p else { var p = String.sub p 0 (String.length p); String.set p 0 chr(ord c - ord 'a' + ord 'A'); p } } exception FileNotFound : string; try { var v = Sys.version; var year : int = neko "$neko_build_year()"; var head = "NekoML Compiler v" + v.maj + "." + v.min + "." + v.build + " - (c)2005-" + year + " Haxe Foundation\n Usage : nekoml [options] files..."; var path = &[""]; var files = &[]; var neko = &false; var std = &Some("nekoml.std"); var pack = &None; var packs = Hashtbl.create(); function use_pack(file) { var data = try IO.file_contents file catch { _ -> throw FileNotFound(file) }; Zip.init(); var h = String.unserialize (Zip.uncompress data); Hashtbl.iter (function(key,m) { if *verbose then printf "Cached %s [%d bytes]\n" (key,String.length m); Hashtbl.add packs key m }) h; } var decl = [ ("-p", Args.String (function(p) { path := complete p :: *path }) , " : additional file search path"); ("-v", Args.Void (function() { verbose := true }) , ": verbose mode"); ("-n", Args.Void (function() { neko := true }) , ": generate intermediate .neko files"); ("-pack", Args.String (function(p) { pack := Some p })," : build module packages"); ("-use", Args.String use_pack," : use this module package"); ("-nostd", Args.Void (function() { path := List.append (*path) ["core/"]; std := None; }),": disable std lib"); ]; var std_path = List.append (*path) Reflect.loader_path(); function rec loop(std,l) { match l { | [] -> if *verbose then printf "%s not found in %s\n" (std,String.concat ":" std_path) | path :: l -> try use_pack (path+std) catch { FileNotFound _ -> loop std l } } } Args.parse head decl (function(f) { files := f :: *files }); match *std { | None -> () | Some std -> loop std std_path } var ctx = context (*path) packs (function(ctx,m,e) { if *neko then gen_neko ctx m e else compile ctx m e }); List.iter (function(file) { if *verbose then printf "Compiling %s\n" file; var modname = String.split (Sys.without_extension file) "/"; var modname = match List.map capitalize modname { | "Core" :: m -> m | m -> m }; ignore(load_module ctx modname Lexer.null_pos); }) (List.rev (*files)); match *pack { | None -> () | Some file -> var h = Hashtbl.create(); Hashtbl.iter (function(key,m) { Hashtbl.add h (String.concat "." key) (IO.file_contents m.file) }) ctx.modules; Zip.init(); var ch = IO.write_file file true; IO.write ch (Zip.compress (String.serialize h) 9); IO.close_out ch; } } catch { | Neko.Lexer.Error (msg,p) -> report Neko.Lexer.error_msg(msg) p | Neko.Parser.Error (msg,p) -> report Neko.Parser.error_msg(msg) p | Neko.Compile.Error (msg,p) -> report Neko.Compile.error_msg(msg) p | Nekoml.Lexer.Error (msg,p) -> report Nekoml.Lexer.error_msg(msg) p | Nekoml.Parser.Error (msg,p) -> report Nekoml.Parser.error_msg(msg) p | Nekoml.Typer.Error (msg,p) -> report Nekoml.Typer.error_msg(msg) p | e -> report string(e) Lexer.null_pos } neko-2-4-0/src/nekoml/Match.nml000066400000000000000000000245251464615675700163240ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* ----- We're using the same algorithm and source code as described in chapter 5.2.4 of "The Zinc experiment" by X. Leroy ( https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.43.6772 ) also described in "The Implementation of Functional Programming Languages" by Simon Peyton Jones, in the Chapter 5 by Philip Wadler ( https://www.microsoft.com/en-us/research/publication/the-implementation-of-functional-programming-languages/ ) */ open Nekoml.Ast; open Nekoml.Type; type complete { Total; Partial; Dubious; } type lambda = match_op type matching = ((pattern list , lambda) list , lambda list) var fully_matched_ref = &(function(cl) { false }) var error_ref = &(function(msg : string,p : pos) { assert() }) function error(msg,p) { (*error_ref) msg p; } var failure = MFailure function handle(l1,l2) { if l2 == MFailure then l1 else if l1 == MFailure then l2 else MHandle l1 l2 } function exec(e) { MExecute e true } function cond(path,lambdas) { MConstants path lambdas } function rec mswitch(path,lambdas) { match lambdas { | (TVoid,m1) :: [] -> m1 | (TVoid,m1) :: l -> MNext m1 (mswitch path l) | _ -> MSwitch path lambdas } } function ewhen(e,e2) { MWhen e e2 } function rec bind(v,p,m) { match m { | MBind (v2,p2,m) -> MBind v2 p2 (bind v p m) | act -> if v == "_" then act else MBind v p act } } function rec junk(p,k,m) { match m { | MBind (v,m,m2) -> MBind v m (junk p k m2) | act -> if k == 0 then act else MJunk p k act } } function rec stream_pattern((p,pos)) { (match p { | PIdent i -> PConst (Ident i) | PConst c -> PConst c | PTuple pl -> PTuple (List.map stream_pattern pl) | PRecord (pr,t) -> PRecord (List.map (function((s,p)) { (s , stream_pattern p) }) pr) t | PConstr (path,name,param) -> PConstr path name (match param { None -> None | Some p -> Some (stream_pattern p) }) | PAlias (s,p) -> PAlias s (stream_pattern p) | PTyped (p,t) -> PTyped (stream_pattern p) t | PStream (s,k) -> PStream s k } , pos) } function rec have_when(m) { match m { | MWhen _ -> true | MBind (_,_,e) -> have_when e | _ -> false } } function t_const(c) { match c { | Int i -> TInt i | String s -> TString s | Float f -> TFloat f | Char c -> TChar c | Ident i -> TIdent i | Bool b -> TBool b | _ -> assert() } } function total(p1,p2) { match (p1 , p2) { | (Total , Total) -> Total | (Partial , _) -> Partial | (_ , Partial) -> Partial | _ -> Dubious } } function partial(p1,p2) { match (p1 , p2) { | (Total , _) -> p2 | (_ , Total) -> Total | _ -> Dubious } } function rec start_by_a_variable((p,_)) { match p { | PAlias (_,p) -> start_by_a_variable p | PIdent _ -> true | _ -> false } } function add_to_match((casel,pathl),cas) { (cas :: casel , pathl) } function make_constant_match(path,cas) { match path { | [] -> assert() | _ :: pathl -> ([cas] , pathl) } } function make_token_match(path,cas) { ([cas] , path) } function make_construct_match(tuple,nargs,pathl,cas) { match pathl { | [] -> assert() | path :: pathl -> function rec make_path(i) { if i >= nargs then pathl else { var k = if tuple then MTuple path i else MField path i; k :: make_path (i + 1) } } ([cas] , make_path 0) } } function make_record_match(args,pathl,t,cas) { match pathl { | [] -> assert() | path :: pathl -> var l = List.fold (function(acc,(f,_)) { MRecordField(path,f,t) :: acc }) pathl (List.rev args); ([cas] , l) } } function add_to_division(make_match,divlist,key,cas) { try { var matchref = List.assoc key divlist; matchref := add_to_match (*matchref) cas; divlist } catch { Not_found -> (key , &(make_match cas)) :: divlist } } function always_add(make_match,divlist,cas) { (TVoid , &(make_match cas)) :: divlist } var lines_of_matching = fst function fully_matched(cl) { (*fully_matched_ref) cl } function flatten(a) { match a { | None -> [] | Some p -> match p { | (PTuple l,_) -> l | _ -> [p] } } } function split_matching(m) { match m { | (_ , []) -> assert() | (casel, (curpath :: endpathl) as pathl) -> function rec split_rec(l) { match l { | ((PTyped (p,_),_) :: l , act) :: rest -> split_rec ((p :: l, act) :: rest) | ((PAlias (v,p),_) :: l , act) :: rest -> split_rec ((p :: l, bind v curpath act) :: rest) | ((PIdent v,_) :: l , act) :: rest -> var vars , others = split_rec rest; (add_to_match vars (l, bind v curpath act) , others) | casel -> (([] , endpathl) , (casel , pathl)) } } split_rec casel } } function divide_matching(m) { match m { | (_ , []) -> assert() | (casel , (curpath :: tailpathl) as pathl) -> function rec divide_rec(l) { match l { | [] -> ([] , [] , ([] , pathl)) | ([],_) :: _ -> assert() | ((PTyped (p,_),_) :: l , act) :: rest -> divide_rec ((p :: l , act) :: rest) | ((PAlias (v,p),_) :: l, act) :: rest -> divide_rec ((p :: l , bind v curpath act) :: rest) | ((PConst c,_) :: l, act) :: rest -> var constant , constrs, others = divide_rec rest; (add_to_division (make_constant_match pathl) constant (t_const c) (l, act), constrs , others) | ((PConstr (path,c,arg),_) :: l,act) :: rest -> var constants , constrs, others = divide_rec rest; var args = flatten arg; (constants , add_to_division (make_construct_match false (List.length args) pathl) constrs (TModule path TConstr(c)) (List.append args l,act) , others) | ((PTuple [],_) :: l,act) :: rest -> var constants , constrs, others = divide_rec rest; (constants , add_to_division (make_constant_match pathl) constrs TVoid (l, act), others) | ((PTuple args,_) :: l,act) :: rest -> var constants , constrs, others = divide_rec rest; (constants , add_to_division (make_construct_match true (List.length args) pathl) constrs TVoid (List.append args l,act) , others) | ((PRecord (args,t),_) :: l,act) :: rest -> var constants , constrs, others = divide_rec rest; (constants , add_to_division (make_record_match args pathl magic(t)) constrs TVoid (List.append (List.map snd args) l,act) , others) | ((PStream ((SPattern p) :: sl,k),pp) :: l,act) :: rest -> var constants , constrs, others = divide_rec rest; (constants , always_add (make_token_match ((MToken curpath k) :: pathl)) constrs (stream_pattern p :: (PStream sl (k+1),pp) :: l, act) , others) | ((PStream ((SMagicExpr ((PTuple _,_) as p,e)) :: sl,k),pp) :: l,act) :: rest -> var constants , constrs, others = divide_rec rest; var ebind = MExecute (mk TConst(TIdent "@tmp") t_void pp) false; (constants , always_add (make_token_match (junk curpath k (MExecute magic(e) false) :: ebind :: pathl)) constrs ((PConst (Ident "@tmp"),pp) :: stream_pattern p :: (PStream sl 0,pp) :: l, act) , others) | ((PStream ((SMagicExpr (p,e)) :: sl,k),pp) :: l,act) :: rest -> var constants , constrs, others = divide_rec rest; (constants , always_add (make_token_match (junk curpath k (MExecute magic(e) false) :: pathl)) constrs (stream_pattern p :: (PStream sl 0,pp) :: l, act) , others) | ((PStream ([],k),pp) :: l,act) :: rest -> var constants , constrs, others = divide_rec rest; (constants , always_add (make_constant_match pathl) constrs (l, junk curpath k act) , others) | casel -> ([] , [] , (casel,pathl)) } } divide_rec casel } } function rec conquer_divided_matching(m) { match m { | [] -> ([], Total, []) | (key, matchref) :: rest -> var l1, p1, u1 = conquer_matching (*matchref); var l2, p2, u2 = conquer_divided_matching rest; ((key , l1) :: l2 , total p1 p2 , List.append u1 u2) } } function rec conquer_matching(m) { match m { | ([] , _) -> (failure , Partial , []) | (([],action) :: rest , k) -> if have_when action then { var a , p , r = conquer_matching (rest,k); (handle action a , p , r) } else (action , Total, rest) | (_ , []) -> assert() | ((p :: _,_) :: _ , _ :: _) when start_by_a_variable p -> var vars , rest = split_matching m; var l1, p1, u1 = conquer_matching vars; var l2, p2, u2 = conquer_matching rest; if p1 == Total then (l1 , Total, List.append u1 lines_of_matching(rest)) else (handle l1 l2 , (if p2 == Total then Total else Dubious) , List.append u1 u2) | (_ , path :: _) -> match divide_matching m { | ([] , [] , vars) -> conquer_matching vars | (consts , [] , vars) -> var l1, _ , u1 = conquer_divided_matching consts; var l2, p2, u2 = conquer_matching vars; (handle (cond path l1) l2 , p2 , List.append u1 u2) | ([] , constrs , vars) -> var l1, p1, u1 = conquer_divided_matching constrs; var l2, p2, u2 = conquer_matching vars; if fully_matched (List.map fst constrs) && p1 == Total then (mswitch path l1 , Total , List.append u1 lines_of_matching(vars)) else (handle (mswitch path l1) l2 , partial p1 p2 , List.append u1 u2) | _ -> assert() } | _ -> assert() } } function make(cases : (pattern list, texpr option, texpr) list,p) { var cases = List.concat (List.map (function((pl,wcond,e)) { var e = exec e; var e = match wcond { None -> e | Some e2 -> ewhen e2 e }; List.map (function(p) { ([p] , e) }) pl }) cases); var m = (cases , [MRoot]); var lambda, partial, unused = conquer_matching m; match unused { | [] -> () | ([] , _ ) :: _ -> error "Some pattern are never matched" p | ((_,p) :: _ , _) :: _ -> error "This pattern is never matched" p}; (partial != Total , lambda) } neko-2-4-0/src/nekoml/Neko.nml000066400000000000000000000420101464615675700161510ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Lexer; open Neko.Ast; open Nekoml.Type; type comparison { Native; Structural; } type context { typectx : Nekoml.Typer.context; current : string list; module_name : string; imports : (string list,string) Hashtbl.t; mutable counter : int; mutable refvars : string Set.t; } function gen_label(ctx) { var c = ctx.counter; ctx.counter := ctx.counter + 1; "l" + string c } function gen_variable(ctx) { var c = ctx.counter; ctx.counter := ctx.counter + 1; "v" + string c } function module_name(m) { "@" + String.concat "_" m } function builtin(name) { (EConst (Builtin name) , null_pos) } function ident(name) { (EConst (Ident name) , null_pos) } function int(n) { (EConst (Int n) , null_pos) } var enull = (EConst Null , null_pos) var core = ["Core"]; function record_index(f,t) { match tlinks false t { | TRecord fl -> function rec loop(i,l) { match l { | [] -> assert() | (ff,_,_) :: l -> if f == ff then i else loop (i+1) l } } loop 0 fl | _ -> assert() } } function construct_id(ctx,m,c) { var m = if m == [] then ctx.current else m; var mod = try Nekoml.Typer.module ctx.typectx m catch { Not_found -> assert() }; var r = try Nekoml.Typer.union mod c catch { Not_found -> assert() }; if r == t_error then Hashtbl.hash (c :: m) else match tlinks false r { | TUnion (_,fl) -> function rec loop(i,l) { match l { | [] -> assert() | (cc,_) :: l -> if c == cc then i else loop (i+1) l } } loop 0 fl | _ -> assert() } } function rec is_fun(t) { match t.texpr { | TNamed (_,_,t) | TLink t -> is_fun t | TPoly | TMono _ | TFun _ -> true | _ -> false } } function call(ret,f,args,p) { if is_fun ret then (ECall (EConst (Builtin "apply"),p) (f :: args) , p) else (ECall f args , p) } function array(args,p) { (ECall (EConst (Builtin "array"),p) args , p) } function block(e) { match fst e { | EBlock _ -> e | _ -> (EBlock [e] , snd e) } } function rec arity(t) { match t.texpr { | TAbstract -> 0 | TTuple tl -> List.length tl | TLink t -> arity t | _ -> 1 } } function comparison(t) { match tlinks true t { | TNamed (["int"],[],_) | TNamed (["char"],[],_) | TNamed (["float"],[],_) | TNamed (["string"],[],_) -> Native | _ -> Structural } } function import(ctx,m,i,p) { if m == ctx.current then (EField ident(ctx.module_name) i,p) else { var key = i :: m; (EConst Ident(try { Hashtbl.find ctx.imports key } catch { Not_found -> var i = "@" + String.concat "_" m + "_" + i; Hashtbl.add ctx.imports key i; i }),p) } } function rec gen_constant(ctx,c,p) { (match c { | TVoid -> EConst Null | TInt n when n < 0 -> EBinop "-" int(0) int(-n) | TInt n -> EConst (Int n) | TFloat s -> EConst (Float s) | TChar c -> EConst Int(ord c) | TString s -> EConst (String s) | TIdent s -> if Set.exists ctx.refvars s then EArray ident(s) int(0) else EConst (Ident s) | TBool b -> EConst (if b then True else False) | TConstr "[]" | TModule (["Core"],TConstr "[]") -> fst (import ctx core "@empty" p) | TConstr "::" | TModule (["Core"],TConstr "::") -> fst (import ctx core "@cons" p) | TConstr s -> EConst (Ident s) | TModule ([],c) -> fst (gen_constant ctx c p) | TModule (m,c) -> fst (import ctx m (match c { TConstr x -> x | TIdent s -> s | _ -> assert() }) p) } , p) } type match_context { ctx : context; out : string; pos : pos; mutable cache : (match_op , string) list; mutable next : string; mutable first : bool; } var no_label = "" function rec gen_match_rec(mctx,fail,next,m) { try ident (List.phys m mctx.cache) catch { Not_found -> var p = mctx.pos; var ctx = mctx.ctx; var gen_rec = gen_match_rec mctx; match m { | MFailure -> var label = if mctx.first && mctx.next != no_label then mctx.next else fail; if label == no_label then (EBlock [] , p) else call t_void (builtin "goto") [ident label] p | MHandle (m1,m2) -> var label = gen_label ctx; var m1 = gen_rec label true m1; var m2 = gen_rec fail next m2; (EBlock [m1; (ELabel label, p); m2] , p) | MRoot -> assert() | MExecute (e,b) -> if !b then { var ematch = (EBinop "==" ident("@exc") (import ctx core "Stream_error" p) , p); var stream_pos = (ECall (import ctx core "stream_pos" p) [gen_rec fail true MRoot] , p); var test = (EBinop "==" ident("@pos") stream_pos , p); var reraise = (EIf (EBinop "&&" ematch test,p) gen_rec(fail,true,MFailure) Some((ECall (builtin "rethrow") [ident "@exc"],p)) , p); mctx.first := false; match e.edecl { | TConst _ -> gen_expr ctx e | _ -> (ETry gen_expr(ctx,e) "@exc" reraise , p) } } else { mctx.first := true; if !next then gen_expr ctx e else { var out = call t_void (builtin "goto") [ident mctx.out] p; (EBlock [gen_expr ctx e;out] , p) } } | MConstants (m,[(TIdent v,m1)]) -> var m = gen_rec fail true m; (EBlock [ (EVars [(v, Some m)] , p); gen_rec fail next m1 ] , p) | MConstants (m,cl) -> var e = gen_rec fail true m; var cases = List.map (function((c,m)) { (gen_constant ctx c p, gen_rec fail next m) }) cl; (ESwitch e cases Some(gen_rec fail next MFailure), p) | MRecordField (m,f,t) -> (EArray gen_rec(fail,true,m) int(record_index f t) , p) | MTuple (m,n) -> (EArray gen_rec(fail,true,m) int(n) , p) | MField (m,n) -> (EArray gen_rec(fail,true,m) int(n + 2) , p) | MNext (m1,m2) -> var old = mctx.next; var label = gen_label ctx; mctx.next := label; var m1 = gen_rec fail true m1; mctx.next := old; var m2 = gen_rec fail next m2; (EBlock [m1; (ELabel label, p); m2] , p) | MSwitch (m,cl) -> var e = (EArray (gen_rec fail true m) int(0) , p); var cases = List.map (function((c,m)) { var path , c = match c { | TModule (path,TConstr c) -> (path , c) | TConstr c -> (ctx.current,c) | _ -> assert() } (int(construct_id ctx path c), gen_rec fail next m) }) cl; (ESwitch e cases Some(gen_rec fail next MFailure), p) | MBind (v,m1,m2) -> var e1 = gen_rec fail true m1; var old = mctx.cache; mctx.cache := (m1,v) :: mctx.cache; var e2 = gen_rec fail next m2; mctx.cache := old; (EBlock [(EVars [(v, Some e1)] , p); e2] , p) | MWhen (e,m) -> var e = gen_expr ctx e; var m = gen_rec fail next m; var fail = gen_rec fail next MFailure; (EIf e m Some(fail), p) | MToken (m,n) -> call t_void (import ctx core "stream_token" p) [gen_rec fail true m; int n] p | MJunk (m,n,m2) -> var m = gen_rec fail true m; mctx.first := false; (EBlock [ call t_void (import ctx core "stream_junk" p) [m; int n] p; gen_rec fail next m2 ] , p) } } } function rec gen_matching(ctx,v,m,p,stream,next,out) { var mctx = { ctx = ctx; cache = [(MRoot,v)]; pos = p; out = out; first = stream; next = no_label; }; var label = (if stream then gen_label ctx else no_label); var e = gen_match_rec mctx label next m; if stream then { var vpos = "@pos"; var stream_pos = (ECall (import ctx core "stream_pos" p) [ident v] , p); var exc = (ECall builtin("throw") [import ctx core "Stream_error" p] , p); (EBlock [(EVars [(vpos , Some stream_pos)] , p); e; (ELabel label , p); exc] , p) } else e } function rec gen_match(ctx,e,m,stream,p) { var out = gen_label ctx; var v = gen_variable ctx; var m = gen_matching ctx v m p stream stream out; var m = (ENext (EVars [(v,Some e)],p) m, p); (EBlock [m; (ELabel out , p)] , p) } function rec gen_constructor(ctx,tname,c,id,t,p) { var field = ident c; var printer = (EConst Ident(tname + "__string"), p); function val_type(t) { match arity t { | 0 -> var make = array [int id;printer] p; (EBinop "=" field make , p) | n -> var args = Array.list (Array.init n (function(n) { "p" + string n })); var build = array (int id :: printer :: List.map (function(a) { (EConst (Ident a) , p) }) args) p; var func = (EFunction args (EBlock [(EReturn (Some build),p)] , p) , p); (EBinop "=" field func , p) } } var export = (EBinop "=" (EField ident(ctx.module_name) c,p) field , p); (ENext val_type(t) export , p) } function rec gen_type_printer(ctx,c,t,p) { var printer = mk TConst(TModule ["Core"] TIdent("@print_union")) t_void p; var e = mk (TCall printer [ mk TConst(TString c) t_string p; mk TConst(TIdent "v") t_void p ]) t_string p; e } function rec gen_type(ctx,name,t,p) { match t.texpr { | TAbstract | TMono _ | TPoly | TRecord _ | TTuple _ | TFun _ | TNamed (_,_,{ texpr = TNamed _ }) -> (EBlock [] , p) | TLink t -> gen_type ctx name t p | TNamed (name,_,t) -> function rec loop(l) { match l { | [] -> assert() | [x] -> x | _ :: l -> loop l } } gen_type ctx (loop name) t p | TUnion (_,constrs) -> var cmatch = gen_match ctx (ident "v") (MSwitch MRoot (List.map (function((c,t)) { var e = gen_type_printer ctx c t p; (TConstr c , MExecute e true) }) constrs)) false p; var printer = (EFunction ["v"] cmatch , p); var i = &(-1); var regs = List.map (function((c,t)) { i := *i + 1; gen_constructor ctx name c (*i) t p }) constrs; (EBlock ((EVars [(name + "__string",Some printer)],p) :: regs) , p) } } function rec gen_binop(ctx,op,e1,e2,p) { function compare(op) { var cmp = (ECall (import ctx core "@compare" p) [gen_expr ctx e1; gen_expr ctx e2] , p); (EBinop op cmp int(0) , p) } function make(op) { (EBinop op gen_expr(ctx,e1) gen_expr(ctx,e2) , p) } function builtin2(op) { (ECall (builtin op) [gen_expr ctx e1; gen_expr ctx e2] , p) } match op { | "and" -> make "&" | "or" -> make "|" | "xor" -> make "+" | "==" | "!=" | ">" | "<" | ">=" | "<=" -> match comparison e1.etype { | Structural -> compare op | Native -> make op } | "===" -> (EBinop "==" builtin2("pcompare") int(0) , p) | "!==" -> (EBinop "!=" builtin2("pcompare") int(0) , p) | ":=" -> match e1.edecl { | TField _ -> make "=" | TArray (a,i) -> (ECall (import ctx core "@aset" p) [gen_expr ctx a; gen_expr ctx i; gen_expr ctx e2] , p) | _ -> (EBinop "=" (EArray gen_expr(ctx,e1) int(0),e1.epos) gen_expr(ctx,e2) , p) } | "/" when is_int e1.etype && is_int e2.etype -> builtin2 "idiv" | _ -> make op } } function rec gen_call(ctx,rt,e,el,p) { match e.edecl { | TConst (TIdent "neko") | TConst (TModule ([],TIdent "neko")) | TConst (TModule (["Core"],TIdent "neko")) -> match el { | [{ edecl = TConst (TString s) }] -> var ch = IO.read_string (String.concat "\"" (String.split s "'")); var lex = Lexer.create Buffer.create(); Lexer.input lex p.psource ch p.pline p.pmin; fst (Neko.Parser.parse lex) | _ -> assert() } | TConst (TConstr "::") | TConst (TModule ([],TConstr "::")) | TConst (TModule (["Core"],TConstr "::")) -> match el { | [e1;e2] -> ECall (EConst (Builtin "array"),p) [int 1;import ctx core "@pcons" p;gen_expr ctx e1;gen_expr ctx e2] | _ -> assert() } | _ -> fst (call rt (gen_expr ctx e) (List.map (gen_expr ctx) el) p) } } function rec gen_expr(ctx,e) { var p = e.epos; var e = match e.edecl { | TConst c -> fst (gen_constant ctx c p) | TBlock el -> EBlock (gen_block ctx el p) | TParenthesis e -> EParenthesis (gen_expr ctx e) | TCall (f,el) -> gen_call ctx e.etype f el p | TField (e,s) -> EArray gen_expr(ctx,e) int(record_index s e.etype) | TArray (e1,e2) -> ECall (import ctx core "@aget" p) [gen_expr ctx e1;gen_expr ctx e2] | TVar ([v],e) -> ctx.refvars := Set.remove ctx.refvars v; EVars [(v , Some (gen_expr ctx e))] | TVar (vl,e) -> var n = &(-1); EVars (("@tmp" , Some (gen_expr ctx e)) :: List.map (function(v) { ctx.refvars := Set.remove ctx.refvars v; n := *n + 1; (v , Some (EArray ident("@tmp") int(*n),p)) }) vl) | TIf (e,e1,e2) -> EIf (gen_expr ctx e) (gen_expr ctx e1) (match e2 { None -> None | Some e2 -> Some (gen_expr ctx e2) }) | TWhile (e1,e2) -> EWhile (gen_expr ctx e1) (gen_expr ctx e2) NormalWhile | TFunction (rflag,name,params,code) -> if name == "_" then EFunction (List.map fst params) block(gen_expr ctx code) else if !rflag then EVars [(name , Some (EFunction (List.map fst params) block(gen_expr ctx code), p))] else EBlock [gen_functions ctx [e] p] | TBinop (op,e1,e2) -> fst (gen_binop ctx op e1 e2 p) | TTupleDecl tl -> fst (array (List.map (gen_expr ctx) tl) p) | TTypeDecl t -> fst (gen_type ctx "" t p) | TMut e -> fst (gen_expr ctx (*e)) | TRecordDecl fl -> var fl = List.sort (function((f1,_),(f2,_)) { compare (record_index f1 e.etype) (record_index f2 e.etype)}) fl; fst(array (List.map (function((_,e)) { gen_expr ctx e }) fl) p) | TListDecl el -> fst (match el { | [] -> array [] p | x :: l -> array [gen_expr ctx x; gen_expr ctx (mk TListDecl(l) e.etype p)] p }) | TUnop (op,e) -> match op { | "-" -> EBinop "-" int(0) gen_expr(ctx,e) | "*" -> EArray gen_expr(ctx,e) int(0) | "!" -> fst (call t_void (builtin "not") [gen_expr ctx e] p) | "&" -> fst (array [gen_expr ctx e] p) | _ -> assert() } | TMatch (e,m,stream) -> fst (gen_match ctx (gen_expr ctx e) m stream p) | TTupleGet (e,n) -> EArray (gen_expr ctx e) int(n) | TErrorDecl (e,t) -> var printer = gen_expr ctx (gen_type_printer ctx e t p); var printer = (EFunction ["v"] (EBlock [printer],p) , p); var printer = (EVars [(e + "__string",Some printer)] , p); ENext printer (gen_constructor ctx e e (Hashtbl.hash (e :: ctx.current)) t p) | TTry (e,m) -> var out = gen_label ctx; var not_exc = (EBinop "!=" (call t_void (builtin "typeof") [ident "@exc"] p) builtin("tarray"),p); var type_check = (EIf not_exc (EBinop "=" ident("@exc") (call t_void (import ctx core "Neko_error" p) [ident "@exc"] p),p) None,p); var matching = gen_matching ctx "@exc" m p false true out; var reraise = call t_void (builtin "rethrow") [ident "@exc"] p; var handle = (EBlock [type_check; matching; reraise;(ELabel out , p)] , p); ETry (gen_expr ctx e) "@exc" handle }; (e,p) } function rec gen_functions(ctx,fl,p) { var ell = &(EVars (List.map (function(e) { match e.edecl { | TFunction (_,"_",params,e) -> ("_" , Some (EFunction (List.map fst params) block(gen_expr ctx e),p)) | TFunction (_,name,_,_) -> ctx.refvars := Set.add ctx.refvars name; (name , Some (array [enull] p)) | _ -> assert() } }) fl) , p); List.iter (function(e) { var p = e.epos; match e.edecl { | TFunction (_,name,params,e) -> if name != "_" then { var e = gen_expr ctx e; var e = (EFunction (List.map fst params) block(e) , p); var e = (EBinop "=" (EArray ident(name) int(0),p) e, p); var e = (EBlock [e; (EBinop "=" ident(name) (EArray ident(name) int(0),p) , p)] , p); ell := (ENext (*ell) e , p); ctx.refvars := Set.remove ctx.refvars name; } | _ -> assert() } }) fl; *ell } function rec gen_block(ctx,el,p) { var old = ctx.refvars; var ell = &[]; function rec loop(fl,l) { match l { | [] -> if fl != [] then ell := gen_functions ctx (List.rev fl) p :: *ell | ({ edecl = TFunction (true,name,p,f) } as e) :: l -> loop (e :: fl) l | { edecl = TMut r } :: l -> loop fl ((*r) :: l) | x :: l -> if fl != [] then ell := gen_functions ctx (List.rev fl) p :: *ell; ell := gen_expr ctx x :: *ell; loop [] l } } loop [] el; ctx.refvars := old; List.rev (*ell) } function generate(tctx,e,deps,idents,mod) { var m = module_name mod; var ctx = { typectx = tctx; current = mod; module_name = m; counter = 0; imports = Hashtbl.create(); refvars = Set.empty(); }; var p = e.epos; var init = (EBinop "=" ident(m) builtin("exports") , p); var deps = List.map (function(m) { var file = file_name m ""; var load = (ECall (EField builtin("loader") "loadmodule",p) [gen_constant ctx (TString file) p;builtin "loader"] , p); (EBinop "=" ident(module_name m) load , p) }) deps; var exports = List.map (function(i) { (EBinop "=" (EField builtin("exports") i,p) ident(i) , p) }) idents; var el = match gen_expr ctx e { | (EBlock el, _) -> el | e -> [e] } var imports = &[]; Hashtbl.iter (function(key,id) { var e = match key { | [] -> assert() | i :: m -> (EBinop "=" ident(id) (EField ident(module_name m) i , p) , p) } imports := e :: *imports; }) ctx.imports; (EBlock (List.append (init :: List.append deps (*imports)) (List.append el exports)) , e.epos) } neko-2-4-0/src/nekoml/Parser.nml000066400000000000000000000351501464615675700165200ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Nekoml.Ast type error_msg { Unexpected : token; Unclosed : string; } exception Error : (error_msg , pos) function error_msg(msg) { match msg { | Unexpected t -> "Unexpected " + s_token t | Unclosed s -> "Unclosed " + s } } var punion = Lexer.punion; function error(m,p) { throw Error(m,p) } function priority(op) { match op { | "+=" | "-=" | "*=" | "/=" | "|=" | "&=" | "^=" | ":=" -> -3 | "&&" | "||" -> -2 | "==" | "!=" | ">" | "<" | "<=" | ">=" | "===" | "!==" | "<>" | "=" -> -1 | "+" | "-" -> 0 | "*" | "/" -> 1 | "or" | "and" | "xor" -> 2 | "<<" | ">>" | "%" | ">>>" -> 3 | _ -> 4 } } function can_swap(_op,op) { var p1 = priority _op; var p2 = priority op; if p1 < p2 then true else if p1 == p2 then op != "::" else false } function rec make_binop(op,e,e2) { var v , p2 = e2; match v { | EBinop (_op,_e,_e2) when can_swap _op op -> var _e = make_binop op e _e; (EBinop _op _e _e2 , punion (pos _e) (pos _e2)) | _ -> (EBinop op e e2 , punion (pos e) (pos e2)) } } function rec make_unop(op,e,p1) { var v , p2 = e; match v { | EBinop (bop,e,e2) -> ( EBinop bop (make_unop op e p1) e2 , punion p1 p2) | _ -> (EUnop op e, punion p1 p2) } } function rec make_list_pat(p,l) { match l { | [] -> (PConstr [] "[]" None , p) | x :: l -> var p = snd x; var params = (PTuple [x;make_list_pat p l] , p); (PConstr [] "::" Some(params) , p) } } function rec make_list(p,l) { match l { | [] -> (EConst Constr("[]") , p) | x :: l -> var p = snd x; (ECall (EConst Constr("::"), p) [x;make_list p l] , p) } } function is_unop(op) { match op { | "-" | "*" | "!" | "&" -> true | _ -> false } } function unclosed(s,p) { error (Unclosed s) p } function mk(e,p) { (e,p) } function rec program(s) { match s { | [< e = expr s; p = program s >] -> e :: p | [< (Semicolon,_); p = program s >] -> p | [< (Eof,_) >] -> [] } } function rec expr(s) { match s { | [< (BraceOpen,p1); e = block1 s >] -> match s { | [< (BraceClose,p2) >] -> mk e punion(p1,p2) | [< (Eof,_) >] -> unclosed "{" p1 } | [< (Keyword Var,p1); (Const (Ident name),_); t = type_opt s; l = vars s; e = expr s >] -> mk EVar((name,t) :: l,e) punion(p1,pos e) | [< (Keyword If,p1); cond = expr s; (Keyword Then,_); e = expr s >] -> match s { | [< (Keyword Else,_); e2 = expr s >] -> mk EIf(cond,e,Some e2) punion(p1,pos e2) | [< >] -> mk EIf(cond,e,None) punion(p1,pos e) } | [< (Keyword Function,p1); r = function_rec s; n = ident_opt s; (ParentOpen _,po); p = parameters_decl s; t = type_opt s; e = expr s >] -> mk EFunction(r,n,p,e,t) punion(p1,pos e) | [< (Keyword Type,p1); pl = type_decl_parameters s; (Const (Ident tname),p2); d , p3 = type_declaration p2 s >] -> mk ETypeDecl(pl,tname,d) punion(p1,p3) | [< (Keyword Exception,p1); (Const (Constr ename),p2); t = type_opt s >] -> mk EErrorDecl(ename,t) punion(p1,p2) | [< (Keyword Match,p1); e = expr s; (BraceOpen,po); pl = patterns_begin s >] -> match s { | [< (BraceClose,pe) >] -> mk EMatch(e,pl) punion(p1,pe) | [< (Eof,_) >] -> unclosed "{" po } | [< (Keyword Try,p1); b = block s; (Keyword Catch,p2); (BraceOpen,po); pl = patterns_begin s >] -> match s { | [< (BraceClose,pe); >] -> mk ETry((EBlock b,punion p1 p2),pl) punion(p1,pe) | [< (Eof,_) >] -> unclosed "{" po } | [< (Keyword While,p1); e = expr s; (BraceOpen,po); b = block s >] -> match s { | [< (BraceClose,pe) >] -> mk EWhile(e,(EBlock b,punion po pe)) punion(po,pe) | [< (Eof,_) >] -> unclosed "{" po } | [< e = expr_short s >] -> e } } function rec expr_short(s) { match s { | [< (ParentOpen _,p1); pl = parameters s >] -> match s { | [< (ParentClose,p2) >] -> expr_next (ETupleDecl pl,punion p1 p2) s | [< (Eof,_) >] -> unclosed "(" p1 } | [< (Binop op,p) >] -> if ! is_unop op then throw Stream_error; match s { | [< e = expr s >] -> expr_next (make_unop op e p) s } | [< (Const (Constr n),p); e = expr_constr n p s >] -> expr_next e s | [< (Const c,p) >] -> expr_next (EConst c,p) s | [< (BracketOpen,p1); b = block s; (BracketClose,p2) >] -> expr_next (make_list (punion p1 p2) b) s } } function rec expr_next(e,s) { match s { | [< (Binop ":",_); t , p = type_path s >] -> expr_next (ETypeAnnot e t,punion (pos e) p) s | [< (ParentOpen false,po); pl = parameters s >] -> match s { | [< (ParentClose,p) >] -> expr_next (ECall e pl,punion (pos e) p) s | [< (Eof,_) >] -> unclosed "(" po } | [< (Dot,_) >] -> match s { | [< (Const (Ident name),p) >] -> expr_next (EField e name,punion (pos e) p) s | [< (BracketOpen,po); e2 = expr s >] -> match s { | [< (BracketClose,p) >] -> expr_next (EArray e e2,punion (pos e) p) s | [< (Eof,_) >] -> unclosed "[" po } } | [< (Binop op,_); e2 = expr s >] -> make_binop op e e2 | [< ep = expr_short s >] -> function rec loop(ep) { var p = pos ep; match fst ep { | EApply (e2,l) -> mk EApply(e,e2 :: l) punion(pos e,p) | EBinop (op,e1,e2) -> mk EBinop(op,loop e1,e2) punion(pos e,p) | _ -> mk EApply(e,[ep]) punion(pos e,pos ep) } } loop ep | [< >] -> e } } function rec expr_constr(n,p,s) { match s { | [< (Dot,_); e , p2 = expr_constr2 s >] -> match e { | EConst ((Ident _) as c) | EConst ((Constr _) as c) -> mk EConst(Module [n] c) punion(p,p2) | EConst (Module (l,c)) -> mk EConst(Module (n :: l) c) punion(p,p2) | _ -> assert() } | [< >] -> mk EConst(Constr n) p } } function rec expr_constr2(s) { match s { | [< (Const (Ident n),p) >] -> mk EConst(Ident n) p | [< (Const (Constr n),p); e = expr_constr n p s >] -> e } } function rec block1(s) { match s { | [< (Const (Ident name),p) >] -> match s { | [< (Binop "=",_); e = expr s; l = record_fields s >] -> ERecordDecl ((name,e) :: l) | [< e = expr_next (EConst (Ident name),p) s; b = block s >] -> EBlock (e :: b) } | [< b = block s >] -> EBlock b } } function rec record_fields(s) { match s { | [< (Const (Ident name),_); (Binop "=",_); e = expr s; l = record_fields s >] -> (name,e) :: l | [< (Semicolon,_); l = record_fields s >] -> l | [< >] -> [] } } function rec vars(s) { match s { | [< (Binop "=",_) >] -> [] | [< (Comma,_); (Const (Ident name),_); t = type_opt s; l = vars s >] -> (name,t) :: l } } function rec block(s) { match s { | [< e = expr s; b = block s >] -> e :: b | [< (Semicolon,_); b = block s >] -> b | [< >] -> [] } } function rec parameters_decl(s) { match s { | [< (Const (Ident name),_) >] -> parameters_decl_next (ANamed name) s | [< (ParentOpen _,_); l = parameters_decl s >] -> parameters_decl_next (ATuple l) s | [< (ParentClose,_) >] -> [] } } function rec parameters_decl_next(acc,s) { match s { | [< (Comma,_); p = parameters_decl s >] -> acc :: p | [< (Binop ":",_); t , _ = type_path s >] -> parameters_decl_next ATyped(acc,t) s | [< (ParentClose,_) >] -> [acc] } } function rec type_opt(s) { match s { | [< (Binop ":",_); t , _ = type_path s >] -> Some t | [< >] -> None } } function rec function_rec(s) { match s { | [< (Const (Ident "rec"),_) >] -> true | [< >] -> false } } function rec ident_opt(s) { match s { | [< (Const (Ident name),_) >] -> Some name | [< >] -> None } } function rec parameters(s) { match s { | [< e = expr s; p = parameters_next s >] -> e :: p | [< >] -> [] } } function rec parameters_next(s) { match s { | [< (Comma,_); p = parameters s >] -> p | [< >] -> [] } } function rec type_path(s) { match s { | [< (Const (Ident tname),p) >] -> type_path_next EType(None,[],tname) p s | [< (Const (Constr m),p); (Dot,_); l = type_path_mod s; (Const (Ident tname),_) >] -> type_path_next EType(None,m :: l,tname) p s | [< (Quote,_); (Const (Ident a),p) >] -> type_path_next (EPoly a) p s | [< (ParentOpen _,_); t , p = type_path s; l , p2 = type_path_list_next p s; (ParentClose,_) >] -> type_path_next (ETuple (t :: l)) p2 s } } function rec type_path_list(p,s) { match s { | [< t , p = type_path s; l , p2 = type_path_list_next p s >] -> mk (t :: l) p2 } } function rec type_path_list_next(p,s) { match s { | [< (Comma,_); t = type_path_list p s >] -> t | [< >] -> ([] , p) } } function rec type_path_next(t,p,s) { match s { | [< (Arrow,_); t2 , p = type_path s >] -> mk EArrow(t,t2) p | [< (Const (Ident tname),p) >] -> type_path_next EType(Some t,[],tname) p s | [< (Const (Constr m),p); (Dot,_); l = type_path_mod s; (Const (Ident tname),_) >] -> type_path_next EType(Some t,m :: l,tname) p s | [< >] -> (t , p) } } function rec type_path_mod(s) { match s { | [< (Const (Constr m),_); (Dot,_); l = type_path_mod s >] -> m :: l | [< >] -> [] } } function rec type_decl_parameters(s) { match s { | [< (Quote,_); (Const (Ident a),_); >] -> [a] | [< (ParentOpen _,_); l = type_decl_plist s; (ParentClose,_); >] -> l | [< >] -> [] } } function rec type_decl_plist(s) { match s { | [< (Quote,_); (Const (Ident a),_); l = type_decl_plist_next s >] -> a :: l } } function rec type_decl_plist_next(s) { match s { | [< (Comma,_); l = type_decl_plist s >] -> l | [< >] -> [] } } function rec type_declaration(p,s) { match s { | [< (BraceOpen,_) >] -> match s { | [< el , p = record_declaration false s >] -> mk ERecord(el) p | [< el , p = union_declaration s >] -> mk EUnion(el) p } | [< (Binop "=",_); t , p = type_path s >] -> mk EAlias(t) p | [< >] -> mk EAbstract p } } function rec record_declaration(mut,s) { match s { | [< (BraceClose,p) >] -> mk [] p | [< (Const (Ident "mutable"),_); l = record_declaration true s >] -> l | [< (Semicolon,_); l = record_declaration false s >] -> l | [< (Const (Ident name),_); (Binop ":",_); t , _ = type_path s; l , p = record_declaration false s >] -> mk ((name,mut,t) :: l) p } } function rec union_declaration(s) { match s { | [< (BraceClose,p) >] -> mk [] p | [< (Semicolon,_); l = union_declaration s >] -> l | [< (Const (Constr name),_); t = type_opt s; l , p = union_declaration s >] -> mk ((name,t) :: l) p } } function rec patterns_begin(s) { match s { | [< (Vertical,_); l = patterns s >] -> l | [< l = patterns s >] -> l } } function rec patterns(s) { match s { | [< p = pattern s; pl = pattern_next s; w = when_clause s; (Arrow,pa); b = block s; l = patterns_begin s >] -> var pat = (p :: pl,w,match b { [e] -> e | _ -> mk EBlock(b) pa }); pat :: l | [< >] -> [] } } function rec pattern_next(s) { match s { | [< (Vertical,_); p = pattern s; l = pattern_next s >] -> p :: l | [< >] -> [] } } function rec pattern(s) { match s { | [< d , p = pattern_decl s >] -> match s { | [< (Const (Ident "as"),_); (Const (Ident v),p2) >] -> mk PAlias(v,(d,p)) punion(p,p2) | [< (Binop "::",_); d2 , p2 = pattern s >] -> mk PConstr([],"::",Some (PTuple [(d,p);(d2,p2)] , punion p p2)) punion(p,p2) | [< t = type_opt s >] -> match t { | None -> mk d p | Some t -> mk PTyped((d,p),t) p } } } } function rec pattern_decl(s) { match s { | [< (ParentOpen _,p1); pl = pattern_tuple s; (ParentClose,p2) >] -> mk PTuple(pl) punion(p1,p2) | [< (BraceOpen,p1); (Const (Ident name),_); (Binop "=",_); p = pattern s; pl = pattern_record s; (BraceClose,p2) >] -> mk PRecord((name,p) :: pl,0) punion(p1,p2) | [< (Const (Constr name),p1); l, name2, p2 = pattern_mod_path name p1 s; p , p3 = pattern_opt p2 s >] -> mk PConstr(l,name2,p) punion(p1,p3) | [< (Const (Ident i),p); >] -> mk PIdent(i) p | [< (Const c,p); >] -> mk PConst(c) p | [< (Binop "-",p1); (Const (Int i),p2) >] -> mk PConst(Int (-i)) punion(p1,p2) | [< (BracketOpen,p1); l = pattern_list s; (BracketClose,p2) >] -> make_list_pat (punion p1 p2) l | [< (StreamOpen,p1); l = stream_list s; (StreamClose,p2) >] -> mk PStream(l,0) punion(p1,p2) } } function rec pattern_mod_path(name,p,s) { match s { | [< (Dot,_); (Const (Constr n),p); l, n2, p2 = pattern_mod_path n p s >] -> (name :: l , n2 , p2) | [< >] -> ([], name, p) } } function rec stream_list(s) { match s { | [< (Const (Ident v),p1) >] -> match s { | [< l = stream_ident_list s; e = expr s >] -> SExpr(v :: l,e) :: stream_next s | [< >] -> SPattern (PIdent v,p1) :: stream_next s } | [< p = pattern s; l = stream_next s >] -> SPattern p :: l | [< >] -> [] } } function rec stream_ident_list(s) { match s { | [< (Comma,_); (Const (Ident v),_); l = stream_ident_list s >] -> v :: l | [< (Binop "=",_) >] -> [] } } function rec stream_next(s) { match s { | [< (Semicolon,_); l = stream_list s >] -> l | [< >] -> [] } } function rec pattern_list(s) { match s { | [< p = pattern s; l = pattern_list_next s >] -> p :: l | [< >] -> [] } } function rec pattern_list_next(s) { match s { | [< (Semicolon,_); l = pattern_list s >] -> l | [< >] -> [] } } function rec pattern_tuple(s) { match s { | [< p = pattern s; l = pattern_tuple_next s >] -> p :: l | [< >] -> [] } } function rec pattern_tuple_next(s) { match s { | [< (Comma,_); l = pattern_tuple s >] -> l | [< >] -> [] } } function rec pattern_record(s) { match s { | [< (Const (Ident name),_); (Binop "=",_); p = pattern s; l = pattern_record s >] -> (name,p) :: l | [< (Semicolon,_); l = pattern_record s >] -> l | [< >] -> [] } } function rec pattern_opt(p,s) { match s { | [< p = pattern s >] -> (Some p , snd p) | [< >] -> (None , p) } } function rec when_clause(s) { match s { | [< (Keyword When,_); e = expr s >] -> Some e | [< >] -> None } } function parse(lex) { var last = &(Eof,Lexer.null_pos); function rec next_token() { var t = Lexer.token lex (*Nekoml.Lexer.token); match fst t { | Comment s | CommentLine s -> next_token() | _ -> last := t; t } }; try { var p = program (stream next_token); mk EBlock(p) Lexer.null_pos } catch { Stream_error -> error Unexpected(fst (*last)) snd(*last) } } neko-2-4-0/src/nekoml/Type.nml000066400000000000000000000201421464615675700162000ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type pos = Lexer.pos type mutflag { Mutable; Immutable; } type t; type type_expr { TAbstract; TMono : int; TPoly; TRecord : (string , mutflag , t) list; TUnion : (int , (string , t) list); TTuple : t list; TLink : t; TFun : (t list , t); TNamed : (string list , t list , t); } type t { mutable tid : int; mutable texpr : type_expr; } type tconstant { TVoid; TInt : int; TBool : bool; TFloat : string; TString : string; TChar : char; TIdent : string; TConstr : string; TModule : (string list, tconstant); } type texpr; type match_op { MRoot; MFailure; MHandle : (match_op, match_op); MExecute : (texpr, bool); MConstants : (match_op, (tconstant, match_op) list); MField : (match_op, int); MTuple : (match_op, int); MToken : (match_op, int); MRecordField : (match_op, string, t); MJunk : (match_op, int, match_op); MSwitch : (match_op, (tconstant, match_op) list); MBind : (string, match_op, match_op); MWhen : (texpr, match_op); MNext : (match_op, match_op); } type texpr_decl { TConst : tconstant; TBlock : texpr list; TParenthesis : texpr; TCall : (texpr, texpr list); TField : (texpr, string); TArray : (texpr, texpr); TVar : (string list, texpr); TIf : (texpr, texpr, texpr option); TFunction : (bool, string, (string, t) list, texpr); TBinop : (string, texpr, texpr); TTupleDecl : texpr list; TTypeDecl : t; TMut : texpr ref; TRecordDecl : (string, texpr) list; TListDecl : texpr list; TUnop : (string, texpr); TMatch : (texpr, match_op, bool); TTry : (texpr, match_op); TTupleGet : (texpr, int); TErrorDecl : (string, t); TWhile : (texpr, texpr); } type texpr { edecl : texpr_decl; etype : t; epos : pos; } type id_gen = int ref function pos(e) { e.epos } function rec tlinks(name,t) { match t.texpr { | TLink t -> tlinks name t | TNamed (_,_,t) when !name -> tlinks name t | _ -> t.texpr } } function etype(name,e) { tlinks name e.etype } function genid(i) { i := *i + 1; *i } function generator() { &0 } function mk(e,t,p) { { edecl = e; etype = t; epos = p; } } var t_abstract = { tid = -1; texpr = TAbstract } function abstract(s) { tid = -1; texpr = TNamed [s] [] t_abstract; } // theses are not really unique since they're declared // also in Core, so it's not possible to use physeq var t_void = abstract "void"; var t_int = abstract "int"; var t_float = abstract "float"; var t_char = abstract "char"; var t_error = abstract "error"; var t_string = abstract "string" var t_bool = { tid = -1; texpr = TNamed ["bool"] [] ({ tid = -1; texpr = TUnion 2 [ ("true",t_abstract); ("false",t_abstract) ]; }); } function rec is_int(t) { match t.texpr { | TNamed(["int"],_,_) -> true | TLink t -> is_int t | _ -> false } } function t_mono(g) { { tid = -2; texpr = TMono (genid g); } } function t_polymorph(g) { { tid = genid g; texpr = TPoly; } } function t_poly(g,name) { var param = t_mono g; ({ tid = genid g; texpr = TNamed [name] [param] ({ tid = -1; texpr = TAbstract }); } , param) } function mk_fun(g,params,ret) { { tid = if List.exists (function(t) { t.tid != -1 }) (ret :: params) then genid g else -1; texpr = TFun params ret; } } function mk_tup(g,l) { { tid = if List.exists (function(t) { t.tid != -1 }) l then genid g else -1; texpr = TTuple l; } } function mk_record(g,fl) { { tid = if List.exists (function((_,_,t)) { t.tid != -1 }) fl then genid g else -1; texpr = TRecord fl; } } function mk_union(g,fl) { { tid = if List.exists (function((_,t)) { t.tid != -1 }) fl then genid g else -1; texpr = TUnion List.length(fl) fl; } } function rec file_name(m : string list,ext) { match m { | [] -> invalid_arg() | [x] -> x + ext | p :: l -> var c = String.get p 0; var p = if c < 'A' || c > 'Z' then p else { var p = String.sub p 0 (String.length p); String.set p 0 chr(ord c - ord 'A' + ord 'a'); p } p + "/" + file_name l ext } } type print_infos { mutable pi_mcount : int; mutable pi_pcount : int; mutable pi_ml : (t, int) list; mutable pi_ph : (int, int) Hashtbl.t; } function s_context() { { pi_mcount = 0; pi_pcount = 0; pi_ml = []; pi_ph = Hashtbl.create(); } } function poly_id(n) { if n < 26 then String.make 1 chr(ord 'a' + n) else string (n - 25) } function s_mutable(m) { match m { | Mutable -> "mutable " | Immutable -> "" } } function rec s_type(ext,h,t) { match t.texpr { | TAbstract -> "" | TMono _ -> sprintf "'_%s" poly_id(try if t.tid != -2 then assert(); List.phys t h.pi_ml; catch { Not_found -> var k = h.pi_mcount; h.pi_mcount := h.pi_mcount + 1; h.pi_ml := (t,k) :: h.pi_ml; k }) | TPoly -> sprintf "'%s" poly_id(try if t.tid == -1 then assert(); Hashtbl.find h.pi_ph t.tid catch { Not_found -> var k = h.pi_pcount; h.pi_pcount := h.pi_pcount + 1; Hashtbl.add h.pi_ph t.tid k; k }) | TRecord fl -> sprintf "{ %s }" (String.concat "; " (List.map (function((f,m,t)) { s_mutable m + f + " : " + s_type false h t }) fl)) | TUnion (_,fl) -> sprintf "{ %s }" (String.concat "; " (List.map (function((f,t)) { f + " : " + s_type false h t }) fl)) | TTuple l -> sprintf "(%s)" (String.concat ", " (List.map (s_type false h) l)) | TLink t -> s_type ext h t | TFun (tl,r) -> var l = String.concat " -> " (List.map (s_fun false h) tl) + " -> "; l + s_type false h r | TNamed (name,params,t) -> var s = match params { | [] -> "" | [p] -> s_type false h p + " " | l -> "(" + String.concat ", " (List.map (s_type false h) l) + ") " }; var name = String.concat "." name; if ext then s + name + " = " + s_type false h t else s + name } } function rec s_fun(ext,h,t) { match t.texpr { | TLink t -> s_fun ext h t | TFun _ -> "(" + s_type ext h t + ")" | _ -> s_type ext h t } } function rec duplicate(g,h,t) { if t.tid < 0 then t else try Hashtbl.find h t.tid catch { Not_found -> var t2 = { tid = genid g; texpr = TAbstract; }; Hashtbl.add h t.tid t2; t2.texpr := match t.texpr { | TAbstract -> TAbstract | TMono _ -> assert() | TPoly -> t2.tid := -2; TMono (genid g) | TRecord tl -> TRecord (List.map (function((n,m,t)) { (n , m, duplicate g h t) }) tl) | TUnion (n,tl) -> TUnion n (List.map (function((n,t)) { (n , duplicate g h t) }) tl) | TTuple tl -> TTuple (List.map (duplicate g h) tl) | TLink t -> TLink (duplicate g h t) | TFun (tl,t) -> TFun (List.map (duplicate g h) tl) (duplicate g h t) | TNamed (n,p,t) -> TNamed n (List.map (duplicate g h) p) (duplicate g h t) }; t2 } } function rec polymorphize(g,mink,t) { if t.tid == -1 then () else match t.texpr { | TAbstract -> () | TMono k -> if k > mink then { t.texpr := TPoly; t.tid := genid g } | TPoly -> () | TRecord fl -> List.iter (function((_,_,t)) { polymorphize g mink t }) fl | TUnion (_,fl) -> List.iter (function((_,t)) { polymorphize g mink t }) fl | TTuple tl -> List.iter (polymorphize g mink) tl | TLink t -> polymorphize g mink t | TFun (tl,t) -> List.iter (polymorphize g mink) tl; polymorphize g mink t | TNamed (_,tl,t) -> List.iter (polymorphize g mink) tl } } neko-2-4-0/src/nekoml/Typer.nml000066400000000000000000000775531464615675700164040ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ open Nekoml.Ast; open Nekoml.Type; type module_context { mutable file : string; path : string list; types : (string,t) Hashtbl.t; constrs : (string, (t, t)) Hashtbl.t; records : (string, (t, t, mutflag)) Hashtbl.t; deps : (string list, module_context) Hashtbl.t; mutable done : bool; mutable idents : (string,t) Map.t; } type context { gen : id_gen; mutable mink : int; mutable functions : (bool, string, texpr ref, t, (string, t) list, expr, t, pos) list; mutable opens : module_context list; mutable curfunction : string; filecache : (string, string) Hashtbl.t; tmptypes : (string, (t, t list, (string,t) Hashtbl.t)) Hashtbl.t; current : module_context; modules : (string list, module_context) Hashtbl.t; classpath : string list; callback : context -> module_context -> texpr -> void; } type error_msg { Cannot_unify : (t, t); Have_no_field : (t, string); Stack : (error_msg, error_msg); Unknown_field : string; Module_not_loaded : module_context; Custom : string; } exception Error : (error_msg , pos); function rec error_msg_loop(h,m) { match m { | Cannot_unify (t1,t2) -> "Cannot unify " + s_type false h t1 + " and " + s_type false h t2 | Have_no_field (t,f) -> s_type false h t + " have no field " + f | Stack (m1,m2) -> error_msg_loop h m1 + "\n " + error_msg_loop h m2 | Unknown_field s -> "Unknown field " + s | Module_not_loaded m -> "Module " + String.concat "." m.path + " require an interface" | Custom s -> s } } function error_msg(m) { error_msg_loop s_context() m } function s_ttype(t) { s_type false s_context() t } function error(m,p) { throw Error(m,p) } Nekoml.Match.error_ref := (function(msg,p) error Custom(msg) p); var verbose = &false; var load_module_ref = &(function(_,_,_) { assert() }); function add_local(ctx,v,t) { if v != "_" then ctx.current.idents := Map.add ctx.current.idents v t } function save_locals(ctx) { ctx.current.idents } function restore_locals(ctx,l) { ctx.current.idents := l } function module(ctx,path) { Hashtbl.find ctx.modules path } function union(m,c) { fst (Hashtbl.find m.constrs c) } function get_module(ctx,path,p) { match path { | [] -> ctx.current | _ -> var m = try Hashtbl.find ctx.modules path catch { Not_found -> (*load_module_ref) ctx path p }; if m != ctx.current then { if !m.done then error (Module_not_loaded m) p; Hashtbl.replace ctx.current.deps m.path m; }; m } } function get_type(ctx,path,name,p) { function rec loop(l) { match l { | [] -> error Custom("Unknown type " + s_path path name) p | m :: l -> try { Hashtbl.find m.types name } catch { Not_found -> loop l } } } match path { | [] -> loop (ctx.current :: ctx.opens); | _ -> loop [get_module ctx path p] } } function get_constr(ctx,path,name,p) { function rec loop(l) { match l { | [] -> error Custom("Unknown constructor " + s_path path name) p | m :: l -> try { var t1, t2 = Hashtbl.find m.constrs name; (if m == ctx.current then [] else m.path , t1, t2) } catch { Not_found -> loop l } } }; match path { | [] -> loop (ctx.current :: ctx.opens) | _ -> loop [get_module ctx path p] } } function get_ident(ctx,path,name,p) { function rec loop(l) { match l { | [] -> error Custom("Unknown identifier " + s_path path name) p | m :: l -> try (if m == ctx.current then [] else m.path , Map.find m.idents name) catch { Not_found -> loop l } } } match path { | [] -> loop (ctx.current :: ctx.opens) | _ -> loop [get_module ctx path p] } } function get_record(ctx,f,p) { function rec loop(l) { match l { | [] -> error (Unknown_field f) p | m :: l -> try Hashtbl.find m.records f catch { Not_found -> loop l } } } var rt , ft , mut = loop (ctx.current :: ctx.opens); var h = Hashtbl.create(); (duplicate ctx.gen h rt, duplicate ctx.gen h ft, mut) } function rec is_tuple(t) { match t.texpr { | TLink t -> is_tuple t | TTuple _ -> true | TNamed(_,_,t) -> is_tuple t | _ -> false } } function rec is_recursive(t1,t2) { if t1 === t2 then true else match t2.texpr { | TAbstract | TMono _ | TPoly -> false | TRecord _ | TUnion _ -> assert() | TTuple tl -> List.exists (is_recursive t1) tl | TLink t -> is_recursive t1 t | TFun (tl,t) -> List.exists (is_recursive t1) tl || is_recursive t1 t | TNamed (_,p,t) -> List.exists (is_recursive t1) p } } function link(ctx,t1,t2,p) { if is_recursive t1 t2 then error Cannot_unify(t1,t2) p; t1.texpr := TLink t2; if t1.tid < 0 then { if t2.tid == -1 then t1.tid := -1 else t1.tid := genid ctx.gen; } else if t2.tid == -1 then t1.tid := -1 } function unify_stack(t1,t2,e) { match e { | Error ((Cannot_unify _) as e, p) -> error (Stack e Cannot_unify(t1,t2)) p | e -> throw e } } function is_alias(t) { match t { | TAbstract | TRecord _ | TUnion _ -> false | TMono _ | TPoly | TTuple _ | TLink _ | TFun _ | TNamed _ -> true } } function rec propagate(k,t) { match t.texpr { | TAbstract | TPoly -> () | TUnion _ | TRecord _ -> assert() | TMono k2 -> if k < k2 then t.texpr := TMono k | TTuple tl -> List.iter (propagate k) tl | TLink t -> propagate k t | TFun (tl,t) -> propagate k t; List.iter (propagate k) tl | TNamed (_,tl,_) -> List.iter (propagate k) tl } } function rec unify(ctx,t1,t2,p) { if t1 == t2 then () else match (t1.texpr , t2.texpr) { | (TLink t , _) -> unify ctx t t2 p | (_ , TLink t) -> unify ctx t1 t p | (TPoly , _) -> link ctx t1 t2 p | (_ , TPoly) -> link ctx t2 t1 p | (TMono k , _) -> link ctx t1 t2 p; propagate k t2 | (_ , TMono k) -> link ctx t2 t1 p; propagate k t1 | (TNamed (n1,p1,_) , TNamed (n2,p2,_)) when n1 == n2 -> try List.iter2 (function(p1,p2) { unify ctx p1 p2 p }) p1 p2 catch { e -> unify_stack t1 t2 e } | (TNamed (_,_,t1) , _) when is_alias t1.texpr -> try unify ctx t1 t2 p catch { e -> unify_stack t1 t2 e } | (_ , TNamed (_,_,t2)) when is_alias t2.texpr -> try unify ctx t1 t2 p catch { e -> unify_stack t1 t2 e } | (TFun (tl1,r1) , TFun (tl2,r2)) when List.length tl1 == List.length tl2 -> try List.iter2 (function(t1,t2) { unify ctx t1 t2 p }) tl1 tl2; unify ctx r1 r2 p; catch { e -> unify_stack t1 t2 e } | (TTuple tl1 , TTuple tl2) when List.length tl1 == List.length tl2 -> try List.iter2 (function(t1,t2) { unify ctx t1 t2 p }) tl1 tl2 catch { e -> unify_stack t1 t2 e } | _ -> error Cannot_unify(t1,t2) p } } function rec type_type(allow,h,ctx,t,p) { match t { | ETuple [] -> assert() | ETuple [t] -> type_type allow h ctx t p | ETuple el -> mk_tup ctx.gen (List.map (function(t) { type_type allow h ctx t p }) el) | EPoly s -> try Hashtbl.find h s catch { Not_found -> if !allow then error Custom("Unbound type variable '" + s) p; var t = t_mono ctx.gen; Hashtbl.add h s t; t } | EType (param,path,name) -> var param = match param { None -> None | Some t -> Some (type_type allow h ctx t p) }; var t = get_type ctx path name p; match t.texpr { | TNamed (_,params,t2) -> var tl = match (params, param) { | ([] , None) -> [] | ([x] , Some t) -> [t] | (l , Some { texpr = TTuple tl }) when List.length tl == List.length l -> tl | _ -> error Custom("Invalid number of type parameters for " + s_path path name) p }; var h = Hashtbl.create(); var t = duplicate ctx.gen h t; var params = List.map (duplicate ctx.gen h) params; List.iter2 (function(pa,t) { unify ctx pa t p }) params tl; t | _ -> assert() } | EArrow _ -> function rec loop(params,t) { match t { | EArrow (ta,tb) -> var ta = type_type allow h ctx ta p; loop (ta :: params) tb | _ -> var t = type_type allow h ctx t p; mk_fun ctx.gen (List.rev params) t } }; loop [] t } } function rec type_constant(ctx,path,c,p) { match c { | Int i -> mk TConst(TInt i) t_int p | Float s -> mk TConst(TFloat s) t_float p | Bool b -> mk TConst(TBool b) t_bool p | String s -> mk TConst(TString s) t_string p | Char c -> mk TConst(TChar c) t_char p | Ident s -> var path , t = get_ident ctx path s p; var t = duplicate ctx.gen Hashtbl.create() t; mk TConst(TModule path TIdent(s)) t p | Constr s -> var path , ut , t = get_constr ctx path s p; var t = duplicate ctx.gen Hashtbl.create() (match t.texpr { | TAbstract -> ut | TTuple tl -> mk_fun ctx.gen tl ut | _ -> mk_fun ctx.gen [t] ut }); mk TConst(TModule path TConstr(s)) t p | Module (path,c) -> type_constant ctx path c p } } type addable { NInt; NFloat; NString; NNan; } function addable(str,e) { match etype true e { | TNamed (["int"],_,_) -> NInt | TNamed (["float"],_,_) -> NFloat | TNamed (["string"],_,_) when str -> NString | _ -> NNan } } function type_binop(ctx,op,e1,e2,p) { function emk(t) { mk TBinop(op,e1,e2) t p }; match op { | "%" | "+" | "-" | "/" | "*" -> var str = (op == "+"); match (addable str e1, addable str e2) { | (NInt , NInt) -> emk t_int | (NFloat , NFloat) | (NInt , NFloat) | (NFloat , NInt) -> emk t_float | (NInt , NString) | (NFloat , NString) | (NString , NInt) | (NString , NFloat) | (NString , NString) -> emk t_string | (NInt , NNan) | (NFloat , NNan) | (NString , NNan) -> unify ctx e2.etype e1.etype (pos e2); emk e1.etype | (NNan , NInt) | (NNan , NFloat) | (NNan , NString) -> unify ctx e1.etype e2.etype (pos e1); emk e2.etype | (NNan , NNan) -> unify ctx e1.etype t_int (pos e1); unify ctx e2.etype t_int (pos e2); emk t_int } | ">>" | ">>>" | "<<" | "and" | "or" | "xor" -> unify ctx e1.etype t_int (pos e1); unify ctx e2.etype t_int (pos e2); emk t_int | "&&" | "||" -> unify ctx e1.etype t_bool (pos e1); unify ctx e2.etype t_bool (pos e2); emk t_bool | "<" | "<=" | ">" | ">=" | "==" | "!=" | "===" | "!==" -> unify ctx e2.etype e1.etype (pos e2); emk t_bool | ":=" -> match e1.edecl { | TArray _ -> unify ctx e2.etype e1.etype (pos e2); emk t_void | TField (e,f) -> match tlinks false e.etype { | TRecord fl -> var _ , mut , _ = try List.find (function((f2,_,_)) { f2 == f }) fl catch { Not_found -> assert() }; if mut == Immutable then error Custom("Field " + f + " is not mutable") pos(e1); unify ctx e2.etype e1.etype (pos e2); emk t_void | _ -> assert(); } | _ -> var t , pt = t_poly ctx.gen "ref"; unify ctx e2.etype pt (pos e2); unify ctx e1.etype t (pos e1); emk t_void } | "::" -> var t , pt = t_poly ctx.gen "list"; unify ctx e1.etype pt (pos e1); unify ctx e2.etype t (pos e2); var c = mk TConst(TConstr "::") t_mono(ctx.gen) p; mk (TCall c [e1;e2]) t p | _ -> error Custom("Invalid operation " + op) p } } function type_unop(ctx,op,e,p) { function emk(t) { mk TUnop(op,e) t p }; match op { | "&" -> var p , pt = t_poly ctx.gen "ref"; unify ctx e.etype pt (pos e); emk p | "*" -> var p , pt = t_poly ctx.gen "ref"; unify ctx e.etype p (pos e); emk pt | "!" -> unify ctx e.etype t_bool (pos e); emk t_bool | "-" -> match addable false e { | NInt -> emk t_int | NFloat -> emk t_float | _ -> unify ctx e.etype t_int (pos e); emk t_int } | _ -> assert() } } function rec type_arg(ctx,h,binds,p,a) { match a { | ATyped (a,t) -> var n , ta = type_arg ctx h binds p a; unify ctx ta (type_type true h ctx t p) p; (n , ta) | ANamed s -> (s , t_mono ctx.gen) | ATuple al -> var aname = "@t" + string (genid ctx.gen); var nl , tl = List.split (List.map (type_arg ctx h binds p) al); var k = &0; List.iter (function(n) { if n != "_" then binds := (aname,*k,n) :: *binds; k := *k + 1; }) nl; (aname , mk_tup ctx.gen tl) } } function register_function(ctx,isrec,name,pl,e,rt,p) { if ctx.functions == [] then ctx.mink := *ctx.gen; var pl = match pl { | [] -> [ATyped (ANamed "_") EType(None,[],"void")] | _ -> pl }; var expr = &(mk (TConst TVoid) t_void p); var h = Hashtbl.create(); var binds = &[]; var el = List.map (type_arg ctx h binds p) pl; var name = match name { None -> "_" | Some n -> n }; var e = match List.rev (*binds) { | [] -> e | l -> var b = List.fold (function(acc,(v,n,v2)) { var tget = ETupleGet (EConst Ident(v),p) n; (EVar [(v2,None)] (tget,p) , p) :: acc }) [e] l; (EBlock b , p) }; var rt = match rt { | None -> t_mono ctx.gen | Some rt -> type_type true h ctx rt p }; var ft = mk_fun ctx.gen (List.map snd el) rt; ctx.functions := (isrec,name,expr,ft,el,e,rt,p) :: ctx.functions; if isrec then add_local ctx name ft; mk (TMut expr) (if name == "_" then ft else t_void) p } function type_format(ctx,s,p) { var types = &[]; var percent = &false; var i = &0; var l = String.length s; while *i < l { var c = String.get s (*i); if *percent then { percent := false; match c { | '%' -> () | 'x' | 'X' | 'd' -> types := t_int :: *types | 'f' -> types := t_float :: *types | 's' -> types := t_string :: *types | 'b' -> types := t_bool :: *types | 'c' -> types := t_char :: *types | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' -> percent := true | _ -> error (Custom "Invalid % sequence") p } } else match c { | '%' -> percent := true | _ -> () } i := *i + 1; }; if *percent then error (Custom "Invalid % sequence") p; match *types { | [] -> t_void | [x] -> x | l -> mk_tup ctx.gen (List.rev l) } } function rec type_functions(ctx) { var l = ctx.functions; if l != [] then { var mink = ctx.mink; ctx.functions := []; var l = List.map (function((isrec,name,expr,ft,el,e,rt,p)) { var locals = save_locals ctx; var func = ctx.curfunction; if name != "_" then { var fname = s_path ctx.current.path name; if *verbose then print ("Typing "+fname+"\n"); ctx.curfunction := fname; } List.iter (function((p,pt)) { add_local ctx p pt }) el; var e = type_expr ctx e; restore_locals ctx locals; ctx.curfunction := func; var ft2 = mk_fun ctx.gen (List.map snd el) e.etype; unify ctx ft ft2 p; expr := mk TFunction(isrec,name,el,e) ft2 p; if !isrec then add_local ctx name ft; (name == "_" , ft2) }) (List.rev l); List.iter (function((anon,t)) { if !anon then polymorphize ctx.gen mink t }) l } } function rec type_expr(ctx,(e,p)) { match e { | EConst c -> type_constant ctx [] c p | EBlock [] -> mk TConst(TVoid) t_void p | EBlock (e :: l) -> var locals = save_locals ctx; var e = type_block ctx e; var el , t = List.fold (function((l,t),e) { unify ctx t t_void (List.hd l).epos; var e = type_block ctx e; (e :: l , e.etype) }) ([e] , e.etype) l; type_functions ctx; restore_locals ctx locals; mk TBlock(List.rev el) t p | EApply (e,el) -> type_expr ctx (ECall e el,p) | ECall ((EConst (Ident "open"),_),[(EConst (Module (m,Constr modname)),p)]) -> ctx.opens := get_module ctx (List.append m [modname]) p :: ctx.opens; mk (TConst TVoid) t_void p | ECall ((EConst (Ident "open"),_),[(EConst (Constr modname),p)]) -> ctx.opens := get_module ctx [modname] p :: ctx.opens; mk (TConst TVoid) t_void p | ECall ((EConst (Ident "assert"),_) as a,[]) -> type_expr ctx (ECall a [(EConst (String p.psource),p);(EConst (Int p.pline),p)], p) | ECall ((EConst (Ident "invalid_arg"),_) as a,[]) -> type_expr ctx (ECall a [(EConst (String ctx.curfunction),p)], p) | ECall ((EConst (Constr "TYPE"),_),[e]) -> var e = type_expr ctx e; printf "%s:(%d): type : %s\n" (p.psource,p.pline,s_type true s_context() e.etype); mk (TParenthesis e) t_void p | ECall (e,el) -> var e = type_expr ctx e; var el = match el { [] -> [(ETupleDecl [],p)] | _ -> el }; var el = List.map (type_expr ctx) el; match etype false e { | TFun (args,r) -> function rec loop(acc,expr,l,tl,r) { match (l , tl) { | (e :: l , t :: tl) -> match tlinks true t { | TNamed (["format"],[param],_) -> match e.edecl { | TConst (TString s) -> var tfmt = type_format ctx s e.epos; unify ctx param tfmt e.epos; | _ -> match tlinks true e.etype { | TNamed (["format"],[param2],_) -> unify ctx param2 param e.epos | _ -> error Custom("Constant string required for format") e.epos } } | _ -> unify ctx e.etype t (pos e) } loop (e :: acc) expr l tl r | ([] , []) -> mk TCall(expr,List.rev acc) r p | ([] , tl) -> mk TCall(expr,List.rev acc) (mk_fun ctx.gen tl r) p | (el , []) -> match tlinks false r { | TFun (args,r2) -> loop [] (mk TCall(expr,List.rev acc) r p) el args r2 | _ -> error Custom("Too many arguments") p } | _ -> assert() } } loop [] e el args r | _ -> var r = t_mono ctx.gen; var f = mk_fun ctx.gen (List.map (function(e) { e.etype }) el) r; unify ctx e.etype f p; mk TCall(e,el) r p } | EField (e,s) -> var e = type_expr ctx e; var t = match etype false e { | TRecord fl -> try { var _ , _ , t = List.find (function((s2,_,_)) { s == s2 }) fl; t } catch { Not_found -> error Have_no_field(e.etype,s) p } | _ -> var r , t , _ = get_record ctx s p; unify ctx e.etype r (pos e); t }; mk TField(e,s) t p | EArray (e,ei) -> var e = type_expr ctx e; var ei = type_expr ctx ei; unify ctx ei.etype t_int (pos ei); var t , pt = t_poly ctx.gen "array"; unify ctx e.etype t (pos e); mk TArray(e,ei) pt p | EVar _ -> error Custom("Variable declaration not allowed outside a block") p | EIf (e,e1,None) -> var e = type_expr ctx e; unify ctx e.etype t_bool (pos e); var e1 = type_expr ctx e1; unify ctx e1.etype t_void (pos e1); mk TIf(e,e1,None) t_void p | EIf (e,e1,Some e2) -> var e = type_expr ctx e; unify ctx e.etype t_bool (pos e); var e1 = type_expr ctx e1; var e2 = type_expr ctx e2; unify ctx e2.etype e1.etype (pos e2); mk TIf(e,e1,Some e2) e1.etype p | EWhile (e1,e2) -> var e1 = type_expr ctx e1; unify ctx e1.etype t_bool (pos e1); var e2 = type_expr ctx e2; unify ctx e2.etype t_void (pos e2); mk TWhile(e1,e2) t_void p | EFunction (isrec,name,pl,e,rt) -> var r = register_function ctx isrec name pl e rt p; type_functions ctx; r | EBinop (op,e1,e2) -> type_binop ctx op (type_expr ctx e1) (type_expr ctx e2) p | ETypeAnnot (e,t) -> var e = type_expr ctx e; var t = type_type true Hashtbl.create() ctx t p; unify ctx e.etype t (pos e); mk e.edecl t p | ETupleDecl [] -> mk TConst(TVoid) t_void p | ETupleDecl [e] -> var e = type_expr ctx e; mk TParenthesis(e) e.etype (pos e) | ETupleDecl el -> var el = List.map (type_expr ctx) el; mk TTupleDecl(el) (mk_tup ctx.gen (List.map (function(e) { e.etype }) el)) p | ETypeDecl (params,tname,decl) -> var fullname = match ctx.current.path { ["Core"] -> [tname] | p -> List.append p [tname]}; var t , tl , h = try { var t , tl , h = Hashtbl.find ctx.tmptypes tname; if decl != EAbstract then Hashtbl.remove ctx.tmptypes tname; if List.length tl != List.length params then error Custom("Invalid number of parameters for type " + tname) p; (t , tl , h) } catch { Not_found -> if Hashtbl.exists ctx.current.types tname then error Custom("Invalid type redefinition of type " + tname) p; var h = Hashtbl.create(); var tl = List.map (function(p) { var t = t_mono ctx.gen; Hashtbl.add h p t; t }) params; var t = { tid = -1; texpr = TNamed fullname tl t_abstract; }; Hashtbl.add ctx.current.types tname t; if decl == EAbstract then Hashtbl.add ctx.tmptypes tname (t,tl,h); (t , tl , h) }; var t2 = match decl { | EAbstract -> t_abstract | EAlias t -> type_type false h ctx t p | ERecord fields -> var fields = List.map (function((f,m,ft)) { var ft = type_type false h ctx ft p; var m = (if m then Mutable else Immutable); Hashtbl.add ctx.current.records f (t,ft,m); (f , m , ft) }) fields; mk_record ctx.gen fields | EUnion constr -> var constr = List.map (function((c,ft)) { var ft = match ft { | None -> t_abstract | Some ft -> type_type false h ctx ft p }; Hashtbl.add ctx.current.constrs c (t,ft); (c , ft) }) constr; mk_union ctx.gen constr }; t.tid := if t2.tid == -1 && params == [] then -1 else genid ctx.gen; t.texpr := TNamed fullname tl t2; polymorphize ctx.gen 0 t; mk TTypeDecl(t) t_void p | ERecordDecl fl -> var s , _ = try List.hd fl catch { _ -> assert() }; var r , _ , _ = get_record ctx s p; var fll = match tlinks false r { | TRecord fl -> fl | _ -> assert() }; var fl2 = &fll; function rec loop(f,l) { match l { | [] -> if List.exists (function((f2,_,_)) { f == f2 }) fll then error Custom("Duplicate declaration for field " + f) p else error Have_no_field(r,f) p | (f2,_,ft) :: l when f == f2 -> (ft , l) | x :: l -> var t , l = loop f l; (t , x :: l) } } var el = List.map (function((f,e)) { var ft , fl2b = loop f (*fl2); fl2 := fl2b; var e = type_expr ctx e; unify ctx e.etype ft (pos e); (f , e) }) fl; List.iter (function((f,_,_)) { error Custom("Missing field " + f + " in record declaration") p; }) (*fl2); mk (TRecordDecl el) r p | EErrorDecl (name,t) -> var t = match t { None -> t_abstract | Some t -> type_type false Hashtbl.create() ctx t p }; Hashtbl.add ctx.current.constrs name (t_error,t); mk TErrorDecl(name,t) t_void p | EUnop (op,e) -> type_unop ctx op (type_expr ctx e) p | EMatch (e,cl) -> var e = type_expr ctx e; var is_stream = match cl { [] -> false | _ -> List.all (function((l,_,_)) { List.all (function((p,_)) { match p { PStream _ -> true | _ -> false }}) l}) cl }; var partial , m , t = type_match ctx e.etype cl p; if !is_stream && partial then error Custom("This matching is not complete") p; mk TMatch(e,m,is_stream) t p | ETry (e,cl) -> var e = type_expr ctx e; var _ , m , t = type_match ctx t_error cl p; unify ctx t e.etype p; mk TTry(e,m) t p | ETupleGet (e,n) -> var e = type_expr ctx e; function try_unify(et) { var t = Array.init (n + 1) (function(_) { t_mono ctx.gen }); unify ctx et (mk_tup ctx.gen (Array.list t)) p; t.[n] } function rec loop(et) { match et.texpr { | TLink et -> loop et | TTuple l -> try List.nth l n catch { _ -> try_unify et } | _ -> try_unify et } } mk TTupleGet(e,n) (loop e.etype) p } } function rec type_block(ctx,x) { var e , p = x; match e { | EVar (vl,e) -> type_functions ctx; var e = type_expr ctx e; function make(v,t) { var t = match t { | None -> t_mono ctx.gen | Some t -> type_type true Hashtbl.create() ctx t p } add_local ctx v t; t } var t = match vl { | [] -> assert() | [(v,t)] -> make v t | _ -> mk_tup ctx.gen (List.map (function((v,t)) { make v t }) vl) } unify ctx t e.etype (pos e); mk TVar(List.map fst vl,e) t_void p | EFunction (true,name,pl,e,rt) -> register_function ctx true name pl e rt p | _ -> type_functions ctx; type_expr ctx x } } function rec type_pattern(ctx,h,h2,set,add,(pat,p)) { function pvar(add,s) { if Set.exists (*set) s then error Custom("This variable is several time in the pattern") p; set := Set.add (*set) s; try Hashtbl.find h s catch { Not_found -> var t = t_mono ctx.gen; Hashtbl.add h s t; if add then add_local ctx s t; t } } var pt , pat = match pat { | PConst c -> (match c { | Int n -> t_int | Float s -> t_float | String s -> t_string | Char c -> t_char | Bool b -> t_bool | Ident _ | Constr _ | Module _ -> assert() } , pat) | PTuple [p] -> var pt , pat = type_pattern ctx h h2 set add p; (pt , fst pat) | PTuple pl -> var pl , patl = List.split (List.map (type_pattern ctx h h2 set add) pl); (mk_tup ctx.gen pl , PTuple patl) | PRecord (fl,_) -> var s = try fst (List.hd fl) catch { _ -> assert() }; var r , _ , _ = get_record ctx s p; var fl = match tlinks false r { | TRecord rl -> List.map (function((f,pat)) { var pt , pat = type_pattern ctx h h2 set add pat; var t = try { var _ , _ , t = List.find (function((f2,_,_)) { f == f2 }) rl; t } catch { Not_found -> error Have_no_field(r,f) p }; unify ctx pt t (snd pat); (f , pat) }) fl | _ -> assert() }; (r , PRecord fl magic(r)) | PIdent s -> (if s == "_" then t_mono ctx.gen else pvar add s , pat) | PConstr (path,s,param) -> var tparam , param = match param { | None -> (None , None ) | Some ((_,p) as param) -> var t , pat = type_pattern ctx h h2 set add param; (Some (p,t) , Some pat) } var path , ut , t = get_constr ctx path s p; match (t.texpr , tparam) { | (TAbstract , None) -> (duplicate ctx.gen Hashtbl.create() ut , PConstr path s param) | (TAbstract , Some _) -> error Custom("Constructor does not take parameters") p | (_ , None) -> error Custom("Constructor require parameters") p | (_ , Some (p,pt)) -> var h = Hashtbl.create(); var ut = duplicate ctx.gen h ut; var t = duplicate ctx.gen h t; var param , pt = match param { | Some (PTuple l,p) when !is_tuple t -> (Some (PTuple [(PTuple l,p)],p) , mk_fun ctx.gen [pt] ut) | Some (PIdent "_",p) -> (param , pt) | _ -> (param , match pt.texpr { TTuple l -> mk_fun ctx.gen l ut | _ -> mk_fun ctx.gen [pt] ut }) } var t = match t.texpr { TTuple l -> mk_fun ctx.gen l ut | _ -> mk_fun ctx.gen [t] ut }; unify ctx t pt p; (ut , PConstr path s param) } | PAlias (s,pat) -> var pt , pat = type_pattern ctx h h2 set false pat; var t = pvar false s; unify ctx pt t (snd pat); (t , PAlias s pat) | PTyped (pat,t) -> var pt , pat = type_pattern ctx h h2 set add pat; unify ctx pt (type_type true h2 ctx t p) p; (pt , PTyped pat t) | PStream (l,k) -> var t , polyt = t_poly ctx.gen "stream"; var locals = save_locals ctx; var l = List.map (function(s) { match s { | SPattern pat -> var t , p = type_pattern ctx h h2 set true pat; unify ctx t polyt (snd p); SPattern p | SExpr ([v],e) -> var e = type_expr ctx e; var t = pvar true v; unify ctx t e.etype e.epos; SMagicExpr (PIdent v,e.epos) magic(e) | SExpr (vl,e) -> var e = type_expr ctx e; var tl = List.map (pvar true) vl; unify ctx (mk_tup ctx.gen tl) e.etype e.epos; var tup = PTuple (List.map (function(v) { (PIdent v, e.epos) }) vl); SMagicExpr (tup,e.epos) magic(e) | SMagicExpr _ -> assert() } }) l; restore_locals ctx locals; (t , PStream l k) } (pt , (pat,p)) } function rec type_match(ctx,t,cl,p) { var ret = t_mono ctx.gen; var cl = List.map (function((pl,wh,pe)) { var first = &true; var h = Hashtbl.create(); var mainset = &Set.empty(); var pl = List.map (function(pat) { var set = &Set.empty(); var pt , pat = type_pattern ctx h Hashtbl.create() set false pat; if *first then { first := false; mainset := *set; } else { var s1 = Set.diff (*set) (*mainset); var s2 = Set.diff (*mainset) (*set); Set.iter (function(s) { error Custom("Variable " + s + " must occur in all patterns") p }) (Set.union s1 s2); } unify ctx pt t p; pat }) pl; var locals = save_locals ctx; Hashtbl.iter (function(v,t) { add_local ctx v t }) h; var wh = match wh { | None -> None | Some e -> var e = type_expr ctx e; unify ctx e.etype t_bool e.epos; Some e } var pe = type_expr ctx pe; unify ctx pe.etype ret (pos pe); restore_locals ctx locals; (pl , wh , pe) }) cl; Nekoml.Match.fully_matched_ref := (function(cl) { match cl { | (TModule(path,TConstr c)) :: l -> var path , ut , t = get_constr ctx path c Lexer.null_pos; if ut == t_error then false else match tlinks false ut { | TUnion (n,_) -> n == List.length cl | _ -> assert() } | TVoid :: _ -> true | _ -> false } }); var partial , m = Nekoml.Match.make cl p; (partial , m , ret) } function module_infos(m) { var h = Hashtbl.create(); var deps = &(if m.path == ["Core"] || Hashtbl.exists m.deps ["Core"] then [] else [["Core"]]); var idents = &[]; Hashtbl.iter (function(_,m) { deps := m.path :: *deps }) m.deps; Map.iter (function(i,t) { idents := i :: *idents }) m.idents; (m.path,*deps,*idents) } function open_file(ctx,m,p) { var file = file_name m ".nml"; function rec loop(l) { match l { | [] -> try { (file, IO.read_string (Hashtbl.find ctx.filecache (String.concat "." m))) } catch { Not_found -> error Custom("File not found " + file) p } | pp :: l -> try { var f = pp + file; (f , IO.read_file f true) } catch { _ -> loop l } } } loop ctx.classpath } function load_module(ctx,m,p) { try Hashtbl.find ctx.modules m catch { Not_found -> var file , ch = open_file ctx m p; var is_core , core = try (false , Hashtbl.find ctx.modules ["Core"]) catch { Not_found -> ctx.current.file := file; (true , ctx.current) } var ctx = { mink = ctx.mink; gen = ctx.gen; modules = ctx.modules; classpath = ctx.classpath; callback = ctx.callback; filecache = ctx.filecache; curfunction = "anonymous"; tmptypes = Hashtbl.create(); functions = []; opens = [core]; current = if is_core then ctx.current else { file = file; path = m; constrs = Hashtbl.create(); records = Hashtbl.create(); types = Hashtbl.create(); done = false; idents = Map.empty(); deps = Hashtbl.create(); } }; Hashtbl.add ctx.modules m ctx.current; var lex = Lexer.create Buffer.create(); Lexer.input lex file ch 1 0; var ast = Nekoml.Parser.parse lex; if *verbose then printf "Parsed %s\n" file; var e = match ast { | (EBlock (e :: l) , p) -> var e = type_block ctx e; var el , t = List.fold (function((l,t),e) { var e = type_block ctx e; (e :: l , e.etype) }) ([e] , e.etype) l; type_functions ctx; mk TBlock(List.rev el) t p | _ -> type_expr ctx ast } if *verbose then printf "Typing done with %s\n" file; ctx.current.done := true; ctx.callback ctx ctx.current e; ctx.current } } function context(cpath,filecache,callb) { var ctx = { gen = generator(); tmptypes = Hashtbl.create(); modules = Hashtbl.create(); filecache = filecache; functions = []; opens = []; mink = 0; classpath = cpath; curfunction = "anonymous"; callback = callb; current = { file = ""; path = ["Core"]; done = false; idents = Map.empty(); constrs = Hashtbl.create(); types = Hashtbl.create(); deps = Hashtbl.create(); records = Hashtbl.create(); }; }; function add_type(args,name,t) { ignore(type_expr ctx (ETypeDecl args name t, Lexer.null_pos)); } function add_variable(name,t) { ctx.current.idents := Map.add ctx.current.idents name t } add_type [] "bool" EUnion([("true",None);("false",None)]); add_type ["a"] "list" (EUnion [("[]",None);("::",Some (ETuple [ EPoly "a"; EType Some(EPoly "a") [] "list"; ]))]); add_variable "neko" (mk_fun ctx.gen [t_polymorph ctx.gen] (t_polymorph ctx.gen)); var core = load_module ctx ["Core"] Lexer.null_pos; ctx } load_module_ref := load_module neko-2-4-0/src/tools/000077500000000000000000000000001464615675700144235ustar00rootroot00000000000000neko-2-4-0/src/tools/Tools.nml000066400000000000000000000040711464615675700162350ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ exception Done; function erase_argument() { neko "$loader.args = $asub($loader.args,1,$asize($loader.args)-1)" } function invalid_arg(f) { throw Args.Invalid } try { var year : int = neko "$neko_build_year()"; var head = "Neko Tools v1.0 - (c)2005-" + year + " Haxe Foundation\nUsage : nekotools [options]"; var decl = [ ("server", Args.Void (function() { erase_argument(); WebServer.init(); throw Done }) , " : start a neko web server"); ("boot", Args.String (function(_) { // separated from boot -c only for help message }) , " : build a standalone executable"); ("boot", Args.String (function(_) { erase_argument(); neko "$loader.loadmodule('tools/nekoboot',$loader)"; throw Done; }) , "-c : build a standalone c program"); ]; Args.parse head decl invalid_arg; } catch { | e -> if e == Done then Sys.exit(0); Stack.dump IO.stderr Stack.exc(); IO.printf IO.stderr "Exception : %s\n" string(e); Sys.exit (-1); }neko-2-4-0/src/tools/WebServer.nml000066400000000000000000000406401464615675700170430ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ type main_function; type client { sock : Net.socket; in : IO.input; out : IO.output; mutable headers : (string, string) list; mutable headers_sent : bool; mutable return_code : (int, string); mutable main : main_function option; } var mime = [ ("gif" , "image/gif"); ("jpeg", "image/jpeg"); ("jpg", "image/jpeg"); ("png", "image/png"); ("css", "text/css"); ("html", "text/html"); ("htm", "text/html"); ("txt", "text/plain"); ("js", "application/javascript"); ("pdf", "application/pdf"); // do we really want that ?? ("xml", "text/xml"); ("wav", "audio/x-wav") ]; var cur_path = &Sys.get_cwd(); var file_log = &None; var module_cache = Hashtbl.create(); var mod_rewrite = &false; function sys_is_file(file) { neko "$loader.loadprim('std@sys_file_type',1)(file)" == "file" } function default_page_404(url) { " 404 Not Found

Not Found

The requested URL "+url+" was not found on this server.


Neko Web DevServer
" } function page_config() { var path = String.concat " - " neko("@List.@make($loader.path)"); " Neko WebServer Config

Neko WebServer Configuration

Visit website" } type http_method { MethodGet; MethodPost; MethodCustom : string; } type http_request { method : http_method; res : string; url : (string, string); version : string; ctype : string option; headers : (string,string) list; mutable params : (string, string) list; mutable post_data : string option; } exception Invalid_char; function invalid_char(l) { throw Invalid_char }; function part(lex,p,l) { var s = Lexer.current lex; String.sub s p (String.length s - (p+l)) } function parse_get_params(str) { var params = String.split str ";"; var params = List.concat (List.map (function(s) { String.split s "&" }) params); List.map (function(p) { match String.split p "=" { | [] -> ("","") | [p] -> (Net.url_decode p,"") | p :: val -> (Net.url_decode p , Net.url_decode (String.concat "=" val)) } }) params; } var http_request_method = Lexer.build [ ("GET", function(l) { MethodGet }); ("POST", function(l) { MethodPost }); ("PUT", function(l) { MethodCustom "PUT" }); ("HEAD", function(l) { MethodCustom "HEAD" }); ("DELETE", function(l) { MethodCustom "DELETE" }); ("TRACE",function(l) { MethodCustom "TRACE" }); ("CONNECT",function(l) { MethodCustom "CONNECT" }); ("OPTIONS",function(l) { MethodCustom "OPTIONS" }); ] invalid_char; var http_request_url = Lexer.build [ (" [^ ]+ ", function(l) { var url = part l 1 1; try { var p = String.find url 0 "?"; var base = String.sub url 0 p; var params = String.sub url (p + 1) (String.length url - (p+1)); (base, params) } catch { Not_found -> (url,"") } }) ] invalid_char; var http_request_version = Lexer.build [ ("HTTP/[0-9.]+\r\n", function(l) { part l 5 2 }); ] invalid_char; var http_header_value = Lexer.build [ ("[^\r]+\r\n", function(l) { part l 0 2 }) ] invalid_char; var http_headers = &Lexer.empty(); http_headers := Lexer.build [ ("\r", function(l) { if Lexer.read l != '\n' then throw Invalid_char; [] }); ("[-A-Za-z0-9_]+: ", function(l) { var s = part l 0 2; var v = Lexer.token l http_header_value; (s,v) :: Lexer.token l (*http_headers) }); ] invalid_char; function header(name,headers) { try { Some snd(List.find (function((n,_)) { String.lowercase n == String.lowercase name }) headers) } catch { Not_found -> None } } function parse_http_request(c) { var l = Lexer.create(); Lexer.input l "http-stream" c.in 1 0; var meth = Lexer.token l http_request_method; var url = Lexer.token l http_request_url; var version = Lexer.token l http_request_version; var headers = Lexer.token l (*http_headers); var params = parse_get_params snd(url); var ctype = header "Content-Type" headers; var post_data, params = if meth == MethodPost && ctype != Some "multipart/form-data" then { var len = match header "Content-Length" headers { None -> 0 | Some len -> int len }; if len >= (1 << 18) then { IO.write c.out "HTTP/1.1 200 OK\r\n"; IO.write c.out "\n\n"; IO.write c.out "Max post data exceeded"; printf "Max post data exceeded (%d bytes)\n" len; throw IO.Eof; } var b = Buffer.create(); var i = &0; while *i < len { Buffer.add_char b (Lexer.read l); i := *i + 1; } var data = Buffer.string b; (Some data , List.append (parse_get_params data) params); } else (None , params); { method = meth; res = Net.url_decode fst(url); url = url; params = params; version = version; headers = headers; post_data = post_data; ctype = ctype; } } function flog(s) { match *file_log { | None -> () | Some f -> IO.write f s; IO.flush f; } } function log( fmt : 'a format, args : 'a ) { var s = sprintf fmt args + "\n"; print s; flog s; } function init_client(s) { var read, write = Net.socket_io s; var ip , port = Net.socket_peer s; { sock = s; in = read; out = write; headers = [("Content-Type","text/html")]; headers_sent = false; return_code = (200,"OK"); main = None; } } function rec find_url_file(url,recursive) { var len = String.length url; if len > 0 && String.get url (len - 1) == '/' then { function rec loop(l) { match l { | [] -> if *mod_rewrite then find_url_file (String.sub url 0 (len - 1)) true else None | f :: l -> match find_url_file(url + f) false { | None -> loop l | x -> x } } } loop ["index.html"; "index.htm"; "index.n"]; } else { var path = if len > 0 && String.get url 0 == '/' then String.sub url 1 (len - 1) else url; var path = *cur_path + path; if Sys.exists path && sys_is_file path then Some path else if recursive && *mod_rewrite then // if path like /path/to/file then lookup /path/to/file.n match find_url_file (url + ".n") false { | Some url -> Some url | None -> // lookup in subdirectory match List.rev String.split(url,"/") { | [] | [_] -> None | _ :: dir -> find_url_file (String.concat "/" List.rev(dir) + "/") true } } else None } } function log_exception(e) { Stack.dump IO.stderr Stack.exc(); IO.printf IO.stderr "Exception : %s\n" string(e); } function print_exception(e) { var stack = Stack.exc(); function format(s) { var s = String.concat "<" (String.split s "<"); var s = String.concat ">" (String.split s ">"); s }; Array.iter (function(s) { match s { | Stack.CFunction -> print "Called from a C function
" | Stack.Module m -> printf "Called from %s (no debug available)
" format(m) | Stack.Pos (file,line) -> printf "Called from %s line %d
" (format file,line) } }) stack var s = string(e); printf "Exception : %s" format(s); } function send_headers(c) { if !c.headers_sent then { c.headers_sent := true; IO.write c.out (sprintf "HTTP/1.1 %d %s\r\n" c.return_code); List.iter (function((name,v)) { IO.write c.out (sprintf "%s: %s\r\n" (name,v)); }) (List.rev c.headers); IO.write c.out "\r\n"; } } function headers_not_sent(c,s) { // should we send an object like mod_neko failure does ? if c.headers_sent then (neko "$throw")("Cannot set "+s+" : Headers already sent"); } function set_header(c,n,v) { headers_not_sent c n; c.headers := (n,v) :: List.filter (function((n2,_)) { n != n2 }) c.headers; } var cur_client : client option ref = &None; var cur_request : http_request option ref = &None; function client() { match *cur_client { | None -> assert() | Some c -> c } } function request() { match *cur_request { | None -> assert() | Some r -> r } } function do_print(v) { var c = client(); try { send_headers(c); var s = string(v); IO.write c.out s; } catch { Neko_error e when magic e == "std@socket_send" -> () } } function init_mod_neko() { // simulate a mod_neko environment var hmethods = Hashtbl.create(); function rec flatten(l) { match l { | [] -> neko "null" | (x,y) :: l -> var l = flatten l; neko "$array(x,y,l)" } } Sys.put_env "MOD_NEKO" "1"; var jit = Sys.get_env "MOD_NEKO_JIT" != None; neko "$loader.loadprim('std@enable_jit',1)(jit)"; function def(name : string,f) { Hashtbl.replace hmethods name magic(f); } function no_param(name,f) { def name neko("function() { f(null) }"); } no_param "get_cookies" (function() { flatten ( match header "Cookie" request().headers { | None -> [] | Some k -> var l = String.split k "; "; List.map (function(k) { match String.split k "=" { | [] | [_] -> (k,"") | k :: l -> (k,String.concat "=" l) } }) l } ) }); def "set_cookie" (function(name,val) { var c = client(); headers_not_sent c "Cookie"; c.headers := ("Set-Cookie",name+"="+val+";") :: c.headers; }); no_param "get_host_name" (function() { Net.host_to_string fst(Net.socket_host client().sock); }); no_param "get_client_ip" (function() { Net.host_to_string fst(Net.socket_peer client().sock); }); no_param "get_uri" (function() { var r = request(); fst r.url }); def "redirect" (function(url) { var c = client(); headers_not_sent c "Redirection"; set_header c "Location" url; c.return_code := (302, "Found"); }); def "set_return_code" (function(i) { var c = client(); headers_not_sent c "Return code"; c.return_code := (i, "OK"); }); def "set_header" (function(name,val) { var c = client(); headers_not_sent c name; set_header c name val; }); def "get_client_header" (function(name) { match header name request().headers { | None -> neko "null" | Some h -> h } }); no_param "get_client_headers" (function() { flatten request().headers }); no_param "get_params_string" (function() { snd request().url }); no_param "get_post_data" (function() { match request().post_data { | None -> neko "null" | Some d -> d } }); no_param "get_params" (function() { flatten request().params }); no_param "cgi_get_cwd" (function() { *cur_path }); def "cgi_set_main" (function(f) { client().main := (if f == neko "null" then None else Some f); }); no_param "cgi_flush" (function() { IO.flush client().out; }); def "parse_multipart_data" (function(part,data) { if request().ctype == Some "multipart/form-data" then (neko "$throw")("parse_multipart_data not implemented"); }); no_param "get_http_method" (function() { match request().method { | MethodGet -> "GET" | MethodPost -> "POST" | MethodCustom r -> r } }); def "log_message" (function (s) { IO.printf IO.stderr "[log] %s\n" s; }); function resolve_method(f:string) { try { Hashtbl.find hmethods f } catch { Not_found -> neko "null" } } var loader = neko "{ args => $array(), path => $loader.path, cache => $loader.cache, loadmodule => function(name,l) { $loader.cache = this.cache; $loader.loadmodule(name,l); }, loadprim => function(prim,nargs) { var l = $ssize(prim); if l > 9 && $ssub(prim,0,9) == 'mod_neko@' { prim = $ssub(prim,9,l-9); var f = resolve_method(prim); if( f == null ) $throw('Unknown mod_neko primitive : '+prim); if( $nargs(f) != nargs ) $throw('Invalid number of arguments for '+prim); return f; } return $loader.loadprim(prim,nargs); } }"; var redirect = neko "$loader.loadprim('std@print_redirect',1)"; var old_cache = neko "$new($loader.cache)"; function(c,r,file) { cur_client := Some c; cur_request := Some r; var main_fun = try { var f = Hashtbl.find module_cache file; c.main := Some f; f } catch { Not_found -> neko "null" }; try { redirect(do_print); if main_fun == neko "null" then neko "loader.loadmodule(file,loader)" else neko "main_fun()"; redirect(neko "null"); } catch { e -> log_exception(e); // stderr try { print_exception(e) } catch { e -> () }; // on screen redirect(neko "null"); } if !c.headers_sent then do_print " "; match c.main { | None -> neko " $loader.cache = $new(old_cache); loader.cache = $loader.cache; "; | Some f -> Hashtbl.add module_cache file f; } } } var mod_neko = init_mod_neko(); function config(c,r) { try { var dir = List.assoc "path" r.params; var old = Sys.get_cwd(); try { Sys.set_cwd dir; cur_path := Sys.get_cwd(); Sys.set_cwd old; } catch { e -> log_exception e } } catch { Not_found -> () } do_print page_config() } function write_page(c,file,ext,code) { IO.write c.out (sprintf "HTTP/1.1 %d %s\r\n" code); var ctype = try { List.assoc ext mime } catch { Not_found -> "unknown/unknown" }; var file_size : int = neko "$loader.loadprim('std@sys_stat',1)(file).size"; IO.write c.out ("Content-Type: " + ctype + "\r\n"); IO.write c.out ("Content-Length: " + file_size + "\r\n"); IO.write c.out "\r\n"; var fin = IO.read_file file true; var bufsize = 100000; var n = &(file_size / bufsize); try { while *n > 0 { IO.write c.out IO.read(fin,bufsize); n := *n - 1; } IO.write c.out IO.read(fin,file_size % bufsize); } catch { e -> IO.close_in fin; throw e } IO.close_in fin } function client_msg(c) { var r = parse_http_request c; cur_client := Some c; if r.res == "/server:config" then config(c,r) else match find_url_file r.res true { | None -> function rec loop(l) { match l { | [] -> c.return_code := (404, "Not Found"); do_print default_page_404(r.res); | f :: l -> match find_url_file f false { | Some page_404 -> var ext = String.lowercase (Sys.extension page_404); write_page c page_404 ext (404, "Not Found") | None -> loop(l) } } } loop ["404.html"; "404.htm"]; | Some file -> var ext = String.lowercase (Sys.extension file); if ext == "n" then { log "Request %s [%s]" (r.res, match r.params { | [] -> "" | l -> "\n " + (String.concat "\n " (List.map (function((p,v)) { p + " => "+ v }) l)) + "\n" }); mod_neko c r file } else { // directly send the file content write_page c file ext (200, "OK"); } } } function client_msg_safe(c) { try { client_msg(c); } catch { | Invalid_char -> printf "Invalid char received\n" (); | IO.Eof -> printf "Connection aborted\n" (); | Neko_error e when Reflect.value e == Reflect.VString "std@socket_send" -> printf "Sending file aborted\n" (); } Net.socket_close c.sock; neko "$loader.loadprim('std@run_gc',1)(true)"; false } function init() { var head = "Neko Web Server v1.0 - (c)2005-2022 Haxe Foundation"; var port = &2000; var host = &"localhost"; var decl = [ ("-p", Args.Int (function(n) { port := n }) , " : change server port"); ("-h", Args.String (function(h) { host := h }) , " : change server host"); ("-d", Args.String (function(d) { var old = Sys.get_cwd(); Sys.set_cwd d; cur_path := Sys.get_cwd(); Sys.set_cwd old; }), " : change the server base directory"); ("-log", Args.String (function(s) { file_log := Some (IO.write_file s true) }), " : set log file"); ("-rewrite", Args.Void (function() { mod_rewrite := true }), ": activate pseudo mod-rewrite for smart urls"); ]; Args.parse head decl (function(f) { throw Args.Invalid }); log "Starting Neko Server on %s:%d" (*host,*port); Net.start_server Net.host_resolve(*host) (*port) init_client client_msg_safe; } neko-2-4-0/src/tools/makedoc.neko000066400000000000000000000035121464615675700167050ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ readdir = $loader.loadprim("std@sys_read_dir",1); command = $loader.loadprim("std@sys_command",1); exec = function(cmd) { $print(cmd,"\n"); var ecode = command(cmd); if( ecode != 0 ) $throw("Error "+ecode+" : aborted"); } makedoc = function(file) { exec("nekoc -o ../www/libs -doc ../"+file); } files = $array( "vm/builtins.c", ); var i = 0; var l = $asize(files); while i < l { makedoc(files[i]); i = i + 1; } var libs = readdir("../libs"); while( libs != null ) { var l = libs[0]; var k = try readdir("../libs/"+l) catch _ { null }; if( l == "mod_tora" ) k = null; while( k != null ) { var f = k[0]; var len = $ssize(f); if( $ssub(f,len-2,2) == ".c" ) makedoc("libs/"+l+"/"+f); k = k[1]; } libs = libs[1]; } neko-2-4-0/src/tools/mod_neko_config.neko000066400000000000000000000064451464615675700204320ustar00rootroot00000000000000begin = " Mod_Neko Configuration

Mod_neko Configuration

"; end = "

Home

"; var params = $loader.loadprim("mod_neko@get_params",0)(); var get_config = $loader.loadprim("mod_neko@cgi_get_config",0); var memory = $loader.loadprim("std@mem_size",1); var execCommand = $loader.loadprim("mod_neko@cgi_command",1); var setHeader = $loader.loadprim("mod_neko@set_header",2); getParam = function(name) { var p = params; while( p != null ) { if( p[0] == name ) return p[1]; p = p[2]; } return null; } error = function(e) { $throw(e); } message = function(msg) { $print(begin); $print("

",msg,"

"); $print(end); } displayObject = function(obj,edit) { var fl = $objfields(obj); var i = 0; $print("
    "); while( i < $asize(fl) ) { var name = $field(fl[i]); var data = $objget(obj,fl[i]); $print("
  • ",name," "); if( edit && $typeof(data) == $tbool ) $print(""); else $print(": ",data); $print("
  • "); i += 1; } $print("
"); } displayVars = function() { var gc = $loader.loadprim("std@gc_stats",0)(); var now = $loader.loadprim("std@date_now",0)(); displayObject({ date => $loader.loadprim("std@date_format",2)(now,null), pid => $loader.loadprim("std@sys_get_pid",0)(), gc_heap => gc.heap, gc_free => gc.free, gc_free_size => $int(gc.free * 100.0 / gc.heap) + "%", },false); } displayConfig = function(cfg) { $print("
"); displayObject(cfg,true); $print(""); $print(""); } displayCache = function(cache) { $print("
Loader path : "+path+"
Server directory :
 
"); $print(""); while( cache != null ) { $print(""); cache = cache[3]; } $print("
FileMemHits
",cache[0],"",memory(cache[1]),"",cache[2],"
"); } displayMenu = function() { $print(begin); $print("

Vars

"); displayVars(); $print("

Config

"); displayConfig(get_config()); $print("

Cache

"); displayCache(execCommand("cache")); $print("

Command

"); $print(""); $print(end); } displayStatistics = function(stats) { $print(begin); $print("

Statistics

"); $print(""); $print(""); while( stats != null ) { $print(""); stats = stats[5]; } $print("
NameTotal TimeInner TimeHitsErrors
",stats[0],"",stats[1],"",stats[2],"",stats[3],"",stats[4],"
"); $print(end); } setConfig = function() { var cfg = get_config(); var fl = $objfields(cfg); var i = 0; while( i < $asize(fl) ) { var name = $field(fl[i]); var data = $objget(cfg,fl[i]); var value = getParam(name); if( $typeof(data) == $tbool ) $objset(cfg,fl[i],value != null); i += 1; } $loader.loadprim("mod_neko@cgi_set_config",1)(cfg); message("Configuration Updated"); } var cmd = getParam("command"); switch( cmd ) { "stats" => displayStatistics(execCommand("stats")) "setconfig" => setConfig() default => { if( cmd != null && cmd != "" ) error("Unknown command "+cmd); displayMenu() } } neko-2-4-0/src/tools/nekoboot.neko000066400000000000000000000217511464615675700171270ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ // primitives var c_src = "#include #include #include #include \"neko_vm.h\" #include \"neko_elf.h\" #ifdef NEKO_WINDOWS # include #else # include #endif #ifdef NEKO_MAC # include # include #endif #ifdef NEKO_BSD # include # include #endif #ifdef NEKO_POSIX # include #endif #define default_loader neko_default_loader unsigned char program[] = %s; unsigned int program_len = %d; unsigned int program_pos = 0; static void report( neko_vm *vm, value exc, int isexc ) { int i; buffer b = alloc_buffer(NULL); value st = neko_exc_stack(vm); for(i=0;i available) mlen = available; memcpy(val_string(str)+val_int(pos), prog, mlen); program_pos += mlen; return alloc_int(mlen); } /* C functions corresponding to the following Neko code : module_read = $loader.loadprim(\"std@module_read\",2); module_exec = $loader.loadprim(\"std@module_exec\",1); module_val = module_read(read_bytecode,$loader); module_exec(module_val); */ int neko_execute_self( neko_vm *vm, value mload ) { value args[] = { alloc_string(\"std@module_read\"), alloc_int(2) }; value args2[] = { alloc_string(\"std@module_exec\"), alloc_int(1) }; value args3[] = { alloc_function(read_bytecode,3,\"boot_read_bytecode\"), mload }; value exc = NULL; value module_read, module_exec, module_val; module_read = val_callEx(mload,val_field(mload,val_id(\"loadprim\")),args,2,&exc); if( exc != NULL ) { report(vm,exc,1); return 1; } module_exec = val_callEx(mload,val_field(mload,val_id(\"loadprim\")),args2,2,&exc); if( exc != NULL ) { report(vm,exc,1); return 1; } module_val = val_callEx(val_null,module_read,args3,2,&exc); if( exc != NULL ) { report(vm,exc,1); return 1; } alloc_field(val_field(mload,val_id(\"cache\")),val_id(\"_self\"),module_val); val_callEx(val_null,module_exec,&module_val,1,&exc); if( exc != NULL ) { report(vm,exc,1); return 1; } return 0; } #ifdef NEKO_POSIX static void handle_signal( int signal ) { if( signal == SIGPIPE ) val_throw(alloc_string(\"Broken pipe\")); else val_throw(alloc_string(\"Segmentation fault\")); } #endif int main( int argc, char *argv[] ) { neko_vm *vm; value mload; int r; neko_global_init(); vm = neko_vm_alloc(NULL); neko_vm_select(vm); mload = default_loader(argv+1,argc-1); r = neko_execute_self(vm,mload); if( mload != NULL && val_field(mload,val_id(\"dump_prof\")) != val_null ) val_ocall0(mload,val_id(\"dump_prof\")); vm = NULL; mload = NULL; neko_vm_select(NULL); neko_global_free(); return r; }"; var elf_update_section_header = $loader.loadprim("std@elf_update_section_header_for_bytecode",3); var file_contents = $loader.loadprim("std@file_contents",1); var file_open = $loader.loadprim("std@file_open",2); var file_write = $loader.loadprim("std@file_write",4); var file_write_char = $loader.loadprim("std@file_write_char",2); var file_close = $loader.loadprim("std@file_close",1); var command = $loader.loadprim("std@sys_command",1); var system = $loader.loadprim("std@sys_string",0)(); var cwd = $loader.loadprim("std@get_cwd",0)(); var get_env = $loader.loadprim("std@get_env",1); var string_split = $loader.loadprim("std@string_split",2); var buffer_new = $loader.loadprim("std@buffer_new",0); var buffer_add = $loader.loadprim("std@buffer_add",2); var buffer_string = $loader.loadprim("std@buffer_string",1); var sprintf = $loader.loadprim("std@sprintf",2); // find a substring from then end var find = function(str,sub,pos) { var l1 = $ssize(str); var l2 = $ssize(sub); var i = l1 - pos; while( i >= 0 ) { if( $ssub(str,i,l2) == sub ) return i; i -= 1; } return null; } // find a file in a path var find_exe_in_path = function(path,file) { while( path != null ) { try { var p = path[0]; var l = $ssize(p); if ( l > 0 ) { // add trailing slash var last = $sget(p,l-1); if ( last != "/" || last != "\\" ) p = p + "/"; } var s = file_contents(p + file); if( $sget(s,0) == 35 ) // '#' $throw("Is a script"); return s; } catch e { path = path[1]; } } $throw("The bootable executable file was not found : "+file); } var find_exe_in_paths = function(paths,file) { var i = 0; var len = $asize(paths); while( i < len ) { try { return find_exe_in_path(paths[i],file); } catch e { i ++= 1; } } $throw("The bootable executable file was not found : "+file); } // bytecode = first argument var args = $loader.args; if( args[0] == "-c" ) { var file = args[1]; var bytecode = file_contents(file); var bytecode_len = $ssize(bytecode); var program_buf = buffer_new(); buffer_add(program_buf, "{"); var i = 0; while(true) { buffer_add(program_buf, $sget(bytecode, i)); i += 1; if (i < bytecode_len) { buffer_add(program_buf, ","); } else { break; } } buffer_add(program_buf, "}"); // write a C source that run the module using neko var c_name = if ($ssub(file, $ssize(file)-2, 2) == ".n") $ssub(file, 0, $ssize(file)-2) + ".c"; else file+".c"; var c_file = file_open(c_name,"wb"); c_src = sprintf(c_src, $array(buffer_string(program_buf), bytecode_len, "%s")); file_write(c_file, c_src, 0, $ssize(c_src)); file_close(c_file); } else { var exe_ext = switch system { "Windows" => ".exe" default => "" }; var boot_exe = "neko" + exe_ext; if( args[0] == "-b" ) { boot_exe = args[1]; args = $asub(args,2,$asize(args)-2); } if( $asize(args) != 1 ) $throw("Need bytecode argument"); var file = args[0]; var bytecode = file_contents(file); // load boot binary var path_sep = switch system { "Windows" => ";" default => ":" } var path = string_split(get_env("PATH"), path_sep); var boot = find_exe_in_paths($array($array(cwd,null),$loader.path,path),boot_exe); var boot_size = $ssize(boot); var dot_pos = find(file,".",1); if( dot_pos != null ) file = $ssub(file,0,dot_pos); // create executable file : // this is the content of boot.bin where is appended // the neko bytecode followed by 'NEKO' and the original exe size var out_name = file+exe_ext; var out = file_open(out_name,"wb"); var bytecode_size = $ssize(bytecode); var pad_size = (4-(boot_size&0x3)) & 0x3; file_write(out,boot,0,boot_size); boot_size += pad_size; if( pad_size >= 3 ) file_write_char(out,0x00); if( pad_size >= 2 ) file_write_char(out,0x00); if( pad_size >= 1 ) file_write_char(out,0x00); file_write(out,bytecode,0,bytecode_size) file_write(out,"NEKO",0,4); file_write_char(out,boot_size & 0xFF); file_write_char(out,(boot_size >> 8) & 0xFF); file_write_char(out,(boot_size >> 16) & 0xFF); file_write_char(out,boot_size >>> 24); file_close(out); // set execution rights switch system { "Windows" => null default => command("chmod 755 "+out_name) } // Update ELF section header (on platforms where that is appropriate) to protect // binary's bytecode from being removed by the strip program var res = elf_update_section_header(out_name,boot_size,bytecode_size+8); if( res == 0 ) $print("Trouble updating elf section header; stripping binary may lead to problems!") } neko-2-4-0/src/tools/test.neko000066400000000000000000000001711464615675700162570ustar00rootroot00000000000000$print("The virtual machine is working !\n"); test = $loader.loadprim("std@test",0); test(); $print("Test successful\n");neko-2-4-0/vm/000077500000000000000000000000001464615675700131165ustar00rootroot00000000000000neko-2-4-0/vm/alloc.c000066400000000000000000000266201464615675700143620ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include "neko.h" #include "objtable.h" #include "opcodes.h" #include "vm.h" #include "neko_mod.h" #include "neko_vm.h" #ifdef NEKO_POSIX # include #endif #ifdef NEKO_WINDOWS #ifdef NEKO_STANDALONE # define GC_NOT_DLL #else # define GC_DLL #endif # define GC_WIN32_THREADS #endif #define GC_THREADS #include "gc/gc.h" #ifndef GC_MALLOC # error Looks like libgc was not installed, please install it before compiling #else // activate to get debug informations about the GC // #define GC_LOG #define gc_alloc GC_MALLOC #define gc_alloc_private GC_MALLOC_ATOMIC #define gc_alloc_big(n) (((n) > 256) ? GC_MALLOC_IGNORE_OFF_PAGE(n) : GC_MALLOC(n)) #define gc_alloc_private_big(n) (((n) > 256) ? GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(n) : GC_MALLOC_ATOMIC(n)) #define gc_alloc_root GC_MALLOC_UNCOLLECTABLE #define gc_free_root GC_FREE typedef struct _klist { const char *name; vkind k; struct _klist *next; } kind_list; static int_val op_last = Last; static value *apply_string = NULL; int_val *callback_return = &op_last; value *neko_builtins = NULL; objtable *neko_fields = NULL; mt_lock *neko_fields_lock = NULL; mt_local *neko_vm_context = NULL; static val_type t_null = VAL_NULL; static val_type t_true = VAL_BOOL; static val_type t_false = VAL_BOOL; EXTERN value val_null = (value)&t_null; EXTERN value val_true = (value)&t_true; EXTERN value val_false = (value)&t_false; static varray empty_array = { VAL_ARRAY, NULL }; static vstring empty_string = { VAL_STRING, 0 }; static kind_list **kind_names = NULL; field id_compare; field id_string; field id_loader; field id_exports; field id_cache; field id_path; field id_loader_libs; field id_get, id_set; field id_add, id_radd, id_sub, id_rsub, id_mult, id_rmult, id_div, id_rdiv, id_mod, id_rmod; EXTERN field neko_id_module; #if defined (GC_LOG) && defined(NEKO_POSIX) static void handle_signal( int signal ) { // reset to default handler struct sigaction act; act.sa_sigaction = NULL; act.sa_handler = SIG_DFL; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGSEGV,&act,NULL); // print signal VM stack printf("**** SIGNAL %d CAUGHT ****\n",signal); neko_vm_dump_stack(neko_vm_current()); // signal again raise(signal); } #endif static void null_warn_proc( char *msg, int arg ) { # ifdef GC_LOG printf(msg,arg); if( strstr(msg,"very large block") ) neko_vm_dump_stack(neko_vm_current()); # endif } void neko_gc_init() { # ifndef NEKO_WINDOWS // we can't set this on windows with old GC since // it's already initialized through its own DllMain GC_all_interior_pointers = 0; # endif #if (GC_VERSION_MAJOR >= 7) && defined(NEKO_WINDOWS) GC_all_interior_pointers = 0; # ifndef NEKO_STANDALONE GC_use_DllMain(); // needed to auto-detect threads created by Apache # endif #endif GC_java_finalization = 1; GC_init(); GC_set_warn_proc((GC_warn_proc)(void*)null_warn_proc); GC_no_dls = 1; #ifdef LOW_MEM GC_dont_expand = 1; #endif GC_clear_roots(); #if defined(GC_LOG) && defined(NEKO_POSIX) { struct sigaction act; act.sa_sigaction = NULL; act.sa_handler = handle_signal; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGSEGV,&act,NULL); } #endif } EXTERN void neko_gc_loop() { GC_collect_a_little(); } EXTERN void neko_gc_major() { GC_gcollect(); } EXTERN void neko_gc_stats( int *heap, int *free ) { *heap = (int)GC_get_heap_size(); *free = (int)GC_get_free_bytes(); } EXTERN char *alloc( unsigned int nbytes ) { return (char*)gc_alloc_big(nbytes); } EXTERN char *alloc_private( unsigned int nbytes ) { return (char*)gc_alloc_private_big(nbytes); } EXTERN value alloc_empty_string( unsigned int size ) { vstring *s; if( size == 0 ) return (value)&empty_string; if( size > max_string_size ) failure("max_string_size reached"); s = (vstring*)gc_alloc_private_big(size+sizeof(vstring)); if( s == NULL ) failure("out of memory"); s->t = VAL_STRING | (size << NEKO_TAG_BITS); (&s->c)[size] = 0; return (value)s; } EXTERN value alloc_string( const char *str ) { if( str == NULL ) return val_null; return copy_string(str,strlen(str)); } EXTERN value alloc_float( tfloat f ) { vfloat *v = (vfloat*)gc_alloc_private(sizeof(vfloat)); v->t = VAL_FLOAT; v->f = f; return (value)v; } EXTERN value alloc_int32( int i ) { vint32 *v = (vint32*)gc_alloc_private(sizeof(vint32)); v->t = VAL_INT32; v->i = i; return (value)v; } EXTERN value alloc_array( unsigned int n ) { value v; if( n == 0 ) return (value)(void*)&empty_array; if( n > max_array_size ) failure("max_array_size reached"); v = (value)gc_alloc_big(sizeof(varray)+(n - 1)*sizeof(value)); if( v == NULL ) failure("out of memory"); v->t = VAL_ARRAY | (n << NEKO_TAG_BITS); return v; } EXTERN value alloc_abstract( vkind k, void *data ) { vabstract *v = (vabstract*)gc_alloc(sizeof(vabstract)); v->t = VAL_ABSTRACT; v->kind = k; v->data = data; return (value)v; } EXTERN value alloc_function( void *c_prim, unsigned int nargs, const char *name ) { vfunction *v; if( c_prim == NULL || ((int)nargs < 0 && nargs != VAR_ARGS) ) failure("alloc_function"); v = (vfunction*)gc_alloc(sizeof(vfunction)); v->t = VAL_PRIMITIVE; v->addr = c_prim; v->nargs = nargs; v->env = alloc_array(0); v->module = alloc_string(name); return (value)v; } value neko_alloc_module_function( void *m, int_val pos, int nargs ) { vfunction *v; if( nargs < 0 && nargs != VAR_ARGS ) failure("alloc_module_function"); v = (vfunction*)gc_alloc(sizeof(vfunction)); v->t = VAL_FUNCTION; v->addr = (void*)pos; v->nargs = nargs; v->env = alloc_array(0); v->module = m; return (value)v; } static value apply1( value p1 ) { value env = NEKO_VM()->env; value *a = val_array_ptr(env) + 1; int n = val_array_size(env) - 1; a[n-1] = p1; return val_callN(a[-1],a,n); } static value apply2( value p1, value p2 ) { value env = NEKO_VM()->env; value *a = val_array_ptr(env) + 1; int n = val_array_size(env) - 1; a[n-2] = p1; a[n-1] = p2; return val_callN(a[-1],a,n); } static value apply3( value p1, value p2, value p3 ) { value env = NEKO_VM()->env; value *a = val_array_ptr(env) + 1; int n = val_array_size(env) - 1; a[n-3] = p1; a[n-2] = p2; a[n-1] = p3; return val_callN(a[-1],a,n); } static value apply4( value p1, value p2, value p3, value p4 ) { value env = NEKO_VM()->env; value *a = val_array_ptr(env) + 1; int n = val_array_size(env) - 1; a[n-4] = p1; a[n-3] = p2; a[n-2] = p3; a[n-1] = p4; return val_callN(a[-1],a,n); } static value apply5( value p1, value p2, value p3, value p4, value p5 ) { value env = NEKO_VM()->env; value *a = val_array_ptr(env) + 1; int n = val_array_size(env) - 1; a[n-4] = p1; a[n-3] = p2; a[n-2] = p3; a[n-1] = p4; a[n-1] = p5; return val_callN(a[-1],a,n); } value neko_alloc_apply( int nargs, value env ) { vfunction *v = (vfunction*)gc_alloc(sizeof(vfunction)); v->t = VAL_PRIMITIVE; switch( nargs ) { case 1: v->addr = apply1; break; case 2: v->addr = apply2; break; case 3: v->addr = apply3; break; case 4: v->addr = apply4; break; case 5: v->addr = apply5; break; default: failure("Too many apply arguments"); break; } v->nargs = nargs; v->env = env; v->module = *apply_string; return (value)v; } EXTERN value alloc_object( value cpy ) { vobject *v; if( cpy != NULL && !val_is_null(cpy) && !val_is_object(cpy) ) val_throw(alloc_string("$new")); // 'new' opcode simulate $new v = (vobject*)gc_alloc(sizeof(vobject)); v->t = VAL_OBJECT; if( cpy == NULL || val_is_null(cpy) ) { v->proto = NULL; otable_init(&v->table); } else { v->proto = ((vobject*)cpy)->proto; otable_copy(&((vobject*)cpy)->table,&v->table); } return (value)v; } EXTERN value copy_string( const char *str, int_val strlen ) { value v = alloc_empty_string((unsigned int)strlen); char *c = (char*)val_string(v); memcpy(c,str,strlen); return v; } EXTERN void alloc_field( value obj, field f, value v ) { otable_replace(&((vobject*)obj)->table,f,v); } static void __on_finalize( value v, void *f ) { ((finalizer)f)(v); } EXTERN void val_gc(value v, finalizer f ) { if( !val_is_abstract(v) ) failure("val_gc"); if( f ) GC_REGISTER_FINALIZER_NO_ORDER(v,(GC_finalization_proc)__on_finalize,f,0,0); else GC_REGISTER_FINALIZER_NO_ORDER(v,NULL,NULL,0,0); } EXTERN value *alloc_root( unsigned int nvals ) { return (value*)gc_alloc_root(nvals*sizeof(value)); } EXTERN void free_root(value *v) { gc_free_root(v); } extern void neko_init_builtins(); extern void neko_init_fields(); extern void neko_init_jit(); extern void neko_free_jit(); #define INIT_ID(x) id_##x = val_id("__" #x) EXTERN void neko_global_init() { # ifdef NEKO_DIRECT_THREADED op_last = neko_get_ttable()[Last]; # endif empty_array.ptr = val_null; neko_gc_init(); neko_vm_context = alloc_local(); neko_fields_lock = alloc_lock(); neko_fields = (objtable*)alloc_root((NEKO_FIELDS_MASK+1) * sizeof(struct _objtable) / sizeof(value)); { int i; for(i=0;i<=NEKO_FIELDS_MASK;i++) otable_init(&neko_fields[i]); } neko_init_builtins(); kind_names = (kind_list**)alloc_root(1); *kind_names = NULL; id_loader = val_id("loader"); id_exports = val_id("exports"); id_cache = val_id("cache"); id_path = val_id("path"); id_loader_libs = val_id("__libs"); neko_id_module = val_id("__module"); INIT_ID(compare); INIT_ID(string); INIT_ID(add); INIT_ID(radd); INIT_ID(sub); INIT_ID(rsub); INIT_ID(mult); INIT_ID(rmult); INIT_ID(div); INIT_ID(rdiv); INIT_ID(mod); INIT_ID(rmod); INIT_ID(get); INIT_ID(set); apply_string = alloc_root(1); *apply_string = alloc_string("apply"); neko_init_jit(); } EXTERN void neko_global_free() { neko_free_jit(); free_root((value*)kind_names); free_root(apply_string); free_root(neko_builtins); free_root((value*)neko_fields); apply_string = NULL; free_local(neko_vm_context); free_lock(neko_fields_lock); neko_gc_major(); } EXTERN void neko_set_stack_base( void *s ) { // deprecated } EXTERN vkind kind_lookup( const char *name ) { kind_list *l = *kind_names; while( l != NULL ) { if( strcmp(l->name,name) == 0 ) return l->k; l = l->next; } return NULL; } EXTERN void kind_share( vkind *k, const char *name ) { kind_list *l = *kind_names; while( l != NULL ) { if( strcmp(l->name,name) == 0 ) { *k = l->k; return; } l = l->next; } l = (kind_list*)alloc(sizeof(kind_list)); l->k = *k; l->name = name; l->next = *kind_names; *kind_names = l; } #endif /* ************************************************************************ */ neko-2-4-0/vm/builtins.c000066400000000000000000001132641464615675700151220ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include "neko.h" #include "objtable.h" #include "vm.h" #ifdef NEKO_MINGW # undef setjmp # define setjmp _setjmp #endif extern value *neko_builtins; DEFINE_KIND(neko_k_kind); DEFINE_KIND(k_old_int32); /**

Builtins

Builtins are basic operations that can be optimized by the Neko compiler.

**/ /**

Array Builtins

**/ /** $array : any* -> array Create an array from a list of values **/ static value builtin_array( value *args, int nargs ) { value a = alloc_array(nargs); int i; for(i=0;i array Create an array of size [n] **/ static value builtin_amake( value size ) { value a; int i,s; val_check(size,int); s = val_int(size); a = alloc_array(s); for(i=0;i array Make a copy of an array **/ static value builtin_acopy( value a ) { int i; value a2; val_check(a,array); a2 = alloc_array(val_array_size(a)); for(i=0;i int Return the size of an array **/ static value builtin_asize( value a ) { val_check(a,array); return alloc_int( val_array_size(a) ); } /** $asub : array -> p:int -> l:int -> array Return [l] elements starting at position [p] of an array. An error occurs if out of array bounds. **/ static value builtin_asub( value a, value p, value l ) { value a2; int i; int pp, ll; val_check(a,array); val_check(p,int); val_check(l,int); pp = val_int(p); ll = val_int(l); if( pp < 0 || ll < 0 || pp+ll < 0 || pp+ll > val_array_size(a) ) neko_error(); a2 = alloc_array(ll); for(i=0;i dst_pos:int -> src:array -> src_pos:int -> len:int -> void Copy [len] elements from [src_pos] of [src] to [dst_pos] of [dst]. An error occurs if out of arrays bounds. **/ static value builtin_ablit( value dst, value dp, value src, value sp, value l ) { int dpp, spp, ll; val_check(dst,array); val_check(dp,int); val_check(src,array); val_check(sp,int); val_check(l,int); dpp = val_int(dp); spp = val_int(sp); ll = val_int(l); if( dpp < 0 || spp < 0 || ll < 0 || dpp + ll < 0 || spp + ll < 0 || dpp + ll > val_array_size(dst) || spp + ll > val_array_size(src) ) neko_error(); memmove(val_array_ptr(dst)+dpp,val_array_ptr(src)+spp,ll * sizeof(value)); return val_null; } /** $aconcat : array array -> array Build a single array from several ones. **/ static value builtin_aconcat( value arrs ) { int tot = 0; int len; int i; value all; val_check(arrs,array); len = val_array_size(arrs); for(i=0;i

String Builtins

**/ /** $string : any -> string Convert any value to a string. This will make a copy of string. **/ static value builtin_string( value v ) { buffer b = alloc_buffer(NULL); val_buffer(b,v); return buffer_to_string(b); } /** $smake : n:int -> string Return an uninitialized string of size [n] **/ static value builtin_smake( value l ) { value v; val_check(l,int); v = alloc_empty_string( val_int(l) ); memset(val_string(v),0,val_int(l)); return v; } /** $ssize : string -> int Return the size of a string **/ static value builtin_ssize( value s ) { val_check(s,string); return alloc_int(val_strlen(s)); } /** $scopy : string -> string Make a copy of a string **/ static value builtin_scopy( value s ) { val_check(s,string); return copy_string( val_string(s), val_strlen(s) ); } /** $ssub : string -> p:int -> l:int -> string Return [l] chars starting at position [p] of a string. An error occurs if out of string bounds. **/ static value builtin_ssub( value s, value p, value l ) { int pp , ll; val_check(s,string); val_check(p,int); val_check(l,int); pp = val_int(p); ll = val_int(l); if( pp < 0 || ll < 0 || pp + ll < 0 || pp + ll > val_strlen(s) ) neko_error(); return copy_string( val_string(s) + pp , ll ); } /** $sget : string -> n:int -> int? Return the [n]th char of a string or [null] if out of bounds **/ static value builtin_sget( value s, value p ) { int pp; val_check(s,string); val_check(p,int); pp = val_int(p); if( pp < 0 || pp >= val_strlen(s) ) return val_null; return alloc_int( (unsigned char)(val_string(s)[pp]) ); } /** $sset : string -> n:int -> c:anyint -> int? Set the [n]th char of a string to ([c] & 255). Returns the char set or [null] if out of bounds. **/ static value builtin_sset( value s, value p, value c ) { int pp; unsigned char cc; val_check(s,string); val_check(p,int); val_check(c,any_int); pp = val_int(p); if( pp < 0 || pp >= val_strlen(s) ) return val_null; cc = (unsigned char)val_any_int(c); val_string(s)[pp] = (char)cc; return alloc_int(cc); } // if our endian value is null, we will use hardware endianness #ifdef NEKO_BIG_ENDIAN # define TO_BE(v) v == val_false #else # define TO_BE(v) v == val_true #endif # define LTB32(bits) bits = (bits >> 24) | ((bits >> 8) & 0xFF00) | ((bits << 8) & 0xFF0000) | (bits << 24) /** $sget16 : string -> n:int -> bigEndian:bool -> int? Return the 16 bit unsigned value at position [n] or [null] if out of bounds **/ static value builtin_sget16( value s, value p, value endian ) { int pp; unsigned short v; val_check(s,string); val_check(p,int); pp = val_int(p); if( pp < 0 || pp+2 > val_strlen(s) ) return val_null; v = *(unsigned short*)(val_string(s)+pp); if( TO_BE(endian) ) v = ((v&0xFF) << 8) | (v>>8); return alloc_int(v); } /** $sset16 : s:string -> n:int -> val:anyint -> bigEndian:bool -> void Set the 16 bit unsigned value at position [n] of string [s] **/ static value builtin_sset16( value s, value p, value val, value endian ) { int pp, v; val_check(s,string); val_check(p,int); val_check(val,any_int); pp = val_int(p); if( pp < 0 || pp+2 > val_strlen(s) ) neko_error(); v = val_any_int(val); if( TO_BE(endian) ) v = ((v&0xFF) << 8) | (v>>8); *(unsigned short*)(val_string(s)+pp) = v; return val_null; } /** $sget32 : string -> n:int -> bigEndian:bool -> anyint? Return the 32 bit int value at position [n] or [null] if out of bounds **/ static value builtin_sget32( value s, value p, value endian ) { int pp; unsigned int v; val_check(s,string); val_check(p,int); pp = val_int(p); if( pp < 0 || pp+4 > val_strlen(s) ) return val_null; v = *(unsigned int*)(val_string(s)+pp); if( TO_BE(endian) ) LTB32(v); return alloc_best_int(v); } /** $sset32 : s:string -> n:int -> val:anyint -> bigEndian:bool -> void Set the 32 bit unsigned value at position [n] of string [s] **/ static value builtin_sset32( value s, value p, value val, value endian ) { int pp, v; val_check(s,string); val_check(p,int); val_check(val,int); pp = val_int(p); if( pp < 0 || pp+4 > val_strlen(s) ) neko_error(); v = val_int(val); if( TO_BE(endian) ) LTB32(v); *(unsigned int*)(val_string(s)+pp) = v; return val_null; } /** $sgetf : string -> n:int -> bigEndian:bool -> float? Return the single precision float value at position [n] or [null] if out of bounds **/ static value builtin_sgetf( value s, value p, value endian ) { int pp; float f; val_check(s,string); val_check(p,int); pp = val_int(p); if( pp < 0 || pp+4 > val_strlen(s) ) return val_null; if( TO_BE(endian) ) { unsigned int bits; bits = *(unsigned int*)(val_string(s)+pp); LTB32(bits); f = *(float*)(&bits); } else f = *(float*)(val_string(s)+pp); return alloc_float(f); } /** $ssetf : s:string -> n:int -> val:float -> bigEndian:bool -> void Set the single precision float value at position [n] of string [s] **/ static value builtin_ssetf( value s, value p, value val, value endian ) { int pp; float f; val_check(s,string); val_check(p,int); val_check(val,float); pp = val_int(p); if( pp < 0 || pp+4 > val_strlen(s) ) neko_error(); f = (float)val_float(val); if( TO_BE(endian) ) { unsigned int bits = *(unsigned int *)&f; LTB32(bits); *(unsigned int*)(val_string(s)+pp) = bits; } else *(float*)(val_string(s)+pp) = f; return val_null; } /** $sgetd : string -> n:int -> float? Return the double precision float value at position [n] or [null] if out of bounds **/ static value builtin_sgetd( value s, value p, value endian ) { int pp; double f; val_check(s,string); val_check(p,int); pp = val_int(p); if( pp < 0 || pp+8 > val_strlen(s) ) return val_null; if( TO_BE(endian) ) { const unsigned char *p = (unsigned char*)(val_string(s) + pp); union { unsigned char bytes[8]; double f; } s; s.bytes[0] = p[7]; s.bytes[1] = p[6]; s.bytes[2] = p[5]; s.bytes[3] = p[4]; s.bytes[4] = p[3]; s.bytes[5] = p[2]; s.bytes[6] = p[1]; s.bytes[7] = p[0]; f = s.f; } else f = *(double*)(val_string(s)+pp); return alloc_float(f); } /** $ssetd : s:string -> n:int -> val:float -> bigEndian:bool -> void Set the double precision float value at position [n] of string [s] **/ static value builtin_ssetd( value s, value p, value val, value endian ) { int pp; double f; val_check(s,string); val_check(p,int); val_check(val,float); pp = val_int(p); if( pp < 0 || pp+8 > val_strlen(s) ) neko_error(); f = (double)val_float(val); if( TO_BE(endian) ) { unsigned char *p = (unsigned char*)(val_string(s) + pp); union { unsigned char bytes[8]; double f; } s; s.f = f; *p = s.bytes[7]; p++; *p = s.bytes[6]; p++; *p = s.bytes[5]; p++; *p = s.bytes[4]; p++; *p = s.bytes[3]; p++; *p = s.bytes[2]; p++; *p = s.bytes[1]; p++; *p = s.bytes[0]; } else *(double*)(val_string(s)+pp) = f; return val_null; } /** // $itof : anyint -> float Convert the low endian bytes integer to the corresponding float value **/ static value builtin_itof( value v, value endian ) { int bits; float f; val_check(v,any_int); bits = val_any_int(v); if( TO_BE(endian) ) LTB32(bits); f = *(float*)&bits; return alloc_float(f); } /** $ftod : float -> anyint Returns the binary integer representation of the float value **/ static value builtin_ftoi( value v, value endian ) { int bits; float f; val_check(v,float); f = (float)val_float(v); bits = *(int *)&f; if( TO_BE(endian) ) LTB32(bits); return alloc_best_int(bits); } /** $itod : low:anyint -> high:anyint -> float Convert the low endian bytes integers to the corresponding double value **/ static value builtin_itod( value low, value high, value endian ) { union { unsigned int i[2]; double d; } s; val_check(low,any_int); val_check(high,any_int) if( TO_BE(endian) ) { unsigned int bits; bits = val_any_int(low); LTB32(bits); s.i[1] = bits; bits = val_any_int(high); LTB32(bits); s.i[0] = bits; } else { s.i[0] = val_any_int(low); s.i[1] = val_any_int(high); } return alloc_float(s.d); } /** $dtoi : float -> anyint array -> void Save the low endian bytes representation of the double value into the array as two any int **/ static value builtin_dtoi( value v, value out, value endian ) { union { unsigned int i[2]; double d; } s; val_check(v,float); val_check(out,array); if( val_array_size(out) < 2 ) neko_error(); s.d = val_float(v); if( TO_BE(endian) ) { unsigned int bits; bits = s.i[0]; LTB32(bits); val_array_ptr(out)[1] = alloc_best_int(bits); bits = s.i[1]; LTB32(bits); val_array_ptr(out)[0] = alloc_best_int(bits); } else { val_array_ptr(out)[0] = alloc_best_int(s.i[0]); val_array_ptr(out)[1] = alloc_best_int(s.i[1]); } return val_null; } /** $isbigendian : void -> bool Tells if we are on a big endian CPU or not. **/ static value builtin_isbigendian() { #ifdef NEKO_BIG_ENDIAN return val_true; #else return val_false; #endif } /** $sblit : dst:string -> dst_pos:int -> src:string -> src_pos:int -> len:int -> void Copy [len] chars from [src_pos] of [src] to [dst_pos] of [dst]. An error occurs if out of strings bounds. **/ static value builtin_sblit( value dst, value dp, value src, value sp, value l ) { int dpp, spp, ll; val_check(dst,string); val_check(dp,int); val_check(src,string); val_check(sp,int); val_check(l,int); dpp = val_int(dp); spp = val_int(sp); ll = val_int(l); if( dpp < 0 || spp < 0 || ll < 0 || dpp + ll < 0 || spp + ll < 0 || dpp + ll > val_strlen(dst) || spp + ll > val_strlen(src) ) neko_error(); memmove(val_string(dst)+dpp,val_string(src)+spp,ll); return val_null; } /** $sfind : src:string -> pos:int -> pat:string -> int? Return the first position starting at [pos] in [src] where [pat] was found. Return null if not found. Error if [pos] is outside [src] bounds. **/ static value builtin_sfind( value src, value pos, value pat ) { int p, l, l2; const char *ptr; val_check(src,string); val_check(pos,int); val_check(pat,string); p = val_int(pos); l = val_strlen(src); l2 = val_strlen(pat); if( p < 0 || p >= l ) neko_error(); ptr = val_string(src) + p; while( l - p >= l2 ) { if( memcmp(ptr,val_string(pat),l2) == 0 ) return alloc_int(p); p++; ptr++; } return val_null; } /**

Object Builtins

**/ /** $new : object? -> object Return a copy of the object or a new object if [null] **/ static value builtin_new( value o ) { if( !val_is_null(o) && !val_is_object(o) ) neko_error(); return alloc_object(o); } /** $objget : o:any -> f:int -> any Return the field [f] of [o] or [null] if doesn't exists or [o] is not an object **/ static value builtin_objget( value o, value f ) { if( !val_is_object(o) ) return val_null; // keep dot-access semantics val_check(f,int); return val_field(o,val_int(f)); } /** $objset : o:any -> f:int -> v:any -> any Set the field [f] of [o] to [v] and return [v] if [o] is an object or [null] if not **/ static value builtin_objset( value o, value f, value v ) { if( !val_is_object(o) ) return val_null; // keep dot-access semantics val_check(f,int); alloc_field(o,val_int(f),v); return v; } /** $objcall : o:any -> f:int -> args:array -> any Call the field [f] of [o] with [args] and return the value or [null] is [o] is not an object **/ static value builtin_objcall( value o, value f, value args ) { if( !val_is_object(o) ) return val_null; // keep dot-access semantics val_check(f,int); val_check(args,array); return val_ocallN(o,val_int(f),val_array_ptr(args),val_array_size(args)); } /** $objfield : o:any -> f:int -> bool Return true if [o] is an object which have field [f] **/ static value builtin_objfield( value o, value f ) { val_check(f,int); return alloc_bool( val_is_object(o) && otable_find(&((vobject*)o)->table, val_int(f)) != NULL ); } /** $objremove : o:object -> f:int -> bool Remove the field [f] from object [o]. Return [true] on success **/ static value builtin_objremove( value o, value f ) { val_check(o,object); val_check(f,int); return alloc_bool( otable_remove(&((vobject*)o)->table,val_int(f)) ); } static void builtin_objfields_rec( value d, field id, void *a ) { *((*(value**)a)++) = alloc_int((int)id); } /** $objfields : o:object -> int array Return all fields of the object **/ static value builtin_objfields( value o ) { value a; value *aptr; objtable *t; val_check(o,object); t = &((vobject*)o)->table; a = alloc_array(otable_count(t)); aptr = val_array_ptr(a); otable_iter(t,builtin_objfields_rec,&aptr); return a; } /** $hash : string -> int Return the hashed value of a field name **/ static value builtin_hash( value f ) { val_check(f,string); return alloc_int( (int)val_id(val_string(f)) ); } /** $fasthash : string -> int Return the hashed value of a field name, without accessing the cache **/ static value builtin_fasthash( value f ) { value acc = alloc_int(0); unsigned char *name; val_check(f,string); name = (unsigned char *)val_string(f); while( *name ) { acc = alloc_int(223 * val_int(acc) + *name); name++; } return acc; } /** $field : int -> string Reverse the hashed value of a field name. Return [null] on failure **/ static value builtin_field( value f ) { val_check(f,int); return val_field_name(val_int(f)); } /** $objsetproto : o:object -> proto:object? -> void Set the prototype of the object **/ static value builtin_objsetproto( value o, value p ) { val_check(o,object); if( val_is_null(p) ) ((vobject*)o)->proto = NULL; else { val_check(p,object); ((vobject*)o)->proto = (vobject*)p; } return val_null; } /** $objgetproto : o:object -> object? Get the prototype of the object **/ static value builtin_objgetproto( value o ) { val_check(o,object); o = (value)((vobject*)o)->proto; if( o == NULL ) return val_null; return o; } /**

Function Builtins

**/ /** $nargs : function -> int Return the number of arguments of a function. If the function have a variable number of arguments, it returns -1 **/ static value builtin_nargs( value f ) { val_check(f,function); return alloc_int( val_fun_nargs(f) ); } /** $call : f:function -> this:any -> args:array -> any Call [f] with [this] context and [args] arguments **/ static value builtin_call( value f, value ctx, value args ) { value old; value ret; neko_vm *vm; val_check(args,array); vm = NEKO_VM(); old = vm->vthis; vm->vthis = ctx; ret = val_callN(f,val_array_ptr(args),val_array_size(args)); vm->vthis = old; return ret; } static value closure_callback( value *args, int nargs ) { value env = NEKO_VM()->env; int cargs = val_array_size(env) - 2; value *a = val_array_ptr(env); value f = a[0]; value o = a[1]; int fargs = val_fun_nargs(f); int i; if( fargs != cargs + nargs && fargs != VAR_ARGS ) return val_null; if( nargs == 0 ) a = val_array_ptr(env) + 2; else if( cargs == 0 ) a = args; else { a = (value*)alloc(sizeof(value)*(nargs+cargs)); for(i=0;i object -> any* -> function Build a closure by applying a given number of arguments to a function **/ static value builtin_closure( value *args, int nargs ) { value f; value env; int fargs; if( nargs <= 1 ) failure("Invalid closure arguments number"); f = args[0]; if( !val_is_function(f) ) neko_error(); fargs = val_fun_nargs(f); if( fargs != VAR_ARGS && fargs < nargs-2 ) failure("Invalid closure arguments number"); env = alloc_array(nargs); memcpy(val_array_ptr(env),args,nargs * sizeof(f)); f = alloc_function( closure_callback, VAR_ARGS, "closure_callback" ); ((vfunction*)f)->env = env; return f; } /** $apply : function -> any* -> any Apply the function to several arguments. Return a function asking for more arguments or the function result if more args needed. **/ static value builtin_apply( value *args, int nargs ) { value f, env; int fargs; int i; nargs--; args++; if( nargs < 0 ) neko_error(); f = args[-1]; if( !val_is_function(f) ) neko_error(); if( nargs == 0 ) return f; fargs = val_fun_nargs(f); if( fargs == nargs || fargs == VAR_ARGS ) return val_callN(f,args,nargs); if( nargs > fargs ) neko_error(); env = alloc_array(fargs + 1); val_array_ptr(env)[0] = f; for(i=0;ienv; value a = alloc_array(nargs); int i; for(i=0;i function Return a variable argument function that, when called, will callback [f] with the array of arguments. **/ static value builtin_varargs( value f ) { value fvar; val_check_function(f,1); fvar = alloc_function(varargs_callback,VAR_ARGS,"varargs"); ((vfunction*)fvar)->env = f; return fvar; } /**

Number Builtins

**/ /** $iadd : anyint -> anyint -> anyint Add two integers **/ static value builtin_iadd( value a, value b ) { return alloc_best_int( val_any_int(a) + val_any_int(b) ); } /** $isub : anyint -> anyint -> anyint Subtract two integers **/ static value builtin_isub( value a, value b ) { return alloc_best_int( val_any_int(a) - val_any_int(b) ); } /** $imult : anyint -> anyint -> anyint Multiply two integers **/ static value builtin_imult( value a, value b ) { return alloc_best_int( val_any_int(a) * val_any_int(b) ); } /** $idiv : anyint -> anyint -> anyint Divide two integers. An error occurs if division by 0 **/ static value builtin_idiv( value a, value b ) { if( val_any_int(b) == 0 ) neko_error(); return alloc_best_int( val_any_int(a) / val_any_int(b) ); } typedef union { double d; struct { unsigned int l; unsigned int h; } i; } qw; /** $isnan : any -> bool Return if a value is the float NaN **/ static value builtin_isnan( value f ) { qw q; unsigned int h, l; if( !val_is_float(f) ) return val_false; q.d = val_float(f); h = q.i.h; l = q.i.l; l = l | (h & 0xFFFFF); h = h & 0x7FF00000; return alloc_bool( h == 0x7FF00000 && l != 0 ); } /** $isinfinite : any -> bool Return if a value is the float +Infinite **/ static value builtin_isinfinite( value f ) { qw q; unsigned int h, l; if( !val_is_float(f) ) return val_false; q.d = val_float(f); h = q.i.h; l = q.i.l; l = l | (h & 0xFFFFF); h = h & 0x7FF00000; return alloc_bool( h == 0x7FF00000 && l == 0 ); } static inline bool has_hex_prefix( const char *c, int len, bool is_signed ) { if (is_signed) return len >= 3 && c[1] == '0' && (c[2] == 'x' || c[2] == 'X'); return len >= 2 && c[0] == '0' && (c[1] == 'x' || c[1] == 'X'); } /** $int : any -> int? Convert the value to the corresponding integer or return [null] **/ static value builtin_int( value f ) { switch( val_type(f) ) { case VAL_FLOAT: #ifdef NEKO_WINDOWS return alloc_best_int((int)val_float(f)); #else // in case of overflow, the result is unspecified by ISO // so we have to make a module 2^32 before casting to int return alloc_int((unsigned int)fmod(val_float(f),4294967296.0)); #endif case VAL_STRING: { char *c = val_string(f), *end; int h; // skip trailing whitespace for hex check while( isspace(*c) ) ++c; char sign = c[0]; bool is_signed = sign == '-' || sign == '+'; if( has_hex_prefix(c,val_strlen(f),is_signed) ) { h = 0; c += is_signed ? 3 : 2; while( *c ) { char k = *c++; if( k >= '0' && k <= '9' ) h = (h << 4) | (k - '0'); else if( k >= 'A' && k <= 'F' ) h = (h << 4) | ((k - 'A') + 10); else if( k >= 'a' && k <= 'f' ) h = (h << 4) | ((k - 'a') + 10); else break; } if( sign == '-' ) h = -h; } else { h = strtol(c,&end,10); if( c == end ) return val_null; } return alloc_best_int(h); } case VAL_INT: case VAL_INT32: return f; } return val_null; } /** $float : any -> float? Convert the value to the corresponding float or return [null] **/ static value builtin_float( value f ) { if( val_is_string(f) ) { char *c = val_string(f), *end; tfloat f = (tfloat)strtod(c,&end); return (c == end) ? val_null : alloc_float(f); } if( val_is_number(f) ) return alloc_float( val_number(f) ); return val_null; } /**

Abstract Builtins

**/ /** $getkind : 'abstract -> 'kind Returns the kind value of the abstract **/ static value builtin_getkind( value v ) { if( val_is_int32(v) ) return alloc_abstract(neko_k_kind,k_old_int32); val_check(v,abstract); return alloc_abstract(neko_k_kind,val_kind(v)); } /** $iskind : any -> 'kind -> bool Tells if a value is of the given kind **/ static value builtin_iskind( value v, value k ) { val_check_kind(k,neko_k_kind); return val_is_abstract(v) ? alloc_bool(val_kind(v) == (vkind)val_data(k)) : (val_data(k) == k_old_int32 ? alloc_bool(val_is_int32(v)) : val_false); } /**

Hashtable Builtins

**/ /** $hkey : any -> int Return the hash of any value **/ static value builtin_hkey( value v ) { return alloc_int(val_hash(v)); } #define HASH_DEF_SIZE 7 /** $hnew : s:int -> 'hash Create an hashtable with [s] slots **/ static value builtin_hnew( value size ) { vhash *h; int i; val_check(size,int); h = (vhash*)alloc(sizeof(vhash)); h->nitems = 0; h->ncells = val_int(size); if( h->ncells <= 0 ) h->ncells = HASH_DEF_SIZE; h->cells = (hcell**)alloc(sizeof(hcell*)*h->ncells); for(i=0;incells;i++) h->cells[i] = NULL; return alloc_abstract(k_hash,h); } static void add_rec( hcell **cc, int size, hcell *c ) { int k; if( c == NULL ) return; add_rec(cc,size,c->next); k = c->hkey % size; c->next = cc[k]; cc[k] = c; } /** $hresize : 'hash -> int -> void Resize an hashtable **/ static value builtin_hresize( value vh, value size ) { vhash *h; hcell **cc; int nsize; int i; val_check_kind(vh,k_hash); val_check(size,int); h = val_hdata(vh); nsize = val_int(size); if( nsize <= 0 ) nsize = HASH_DEF_SIZE; cc = (hcell**)alloc(sizeof(hcell*)*nsize); memset(cc,0,sizeof(hcell*)*nsize); for(i=0;incells;i++) add_rec(cc,nsize,h->cells[i]); h->cells = cc; h->ncells = nsize; return val_null; } /** $hget : 'hash -> k:any -> cmp:function:2? -> any Look for the value bound to the key [k] in the hashtable. Use the comparison function [cmp] or [$compare] if [null]. Return [null] if no value is found. **/ static value builtin_hget( value vh, value key, value cmp ) { vhash *h; hcell *c; if( !val_is_null(cmp) ) val_check_function(cmp,2); val_check_kind(vh,k_hash); h = val_hdata(vh); c = h->cells[val_hash(key) % h->ncells]; if( val_is_null(cmp) ) { while( c != NULL ) { if( val_compare(key,c->key) == 0 ) return c->val; c = c->next; } } else { while( c != NULL ) { if( val_call2(cmp,key,c->key) == alloc_int(0) ) return c->val; c = c->next; } } return val_null; } /** $hmem : 'hash -> k:any -> cmp:function:2? -> bool Look for the value bound to the key [k] in the hashtable. Use the comparison function [cmp] or [$compare] if [null]. Return true if such value exists, false either. **/ static value builtin_hmem( value vh, value key, value cmp ) { vhash *h; hcell *c; if( !val_is_null(cmp) ) val_check_function(cmp,2); val_check_kind(vh,k_hash); h = val_hdata(vh); c = h->cells[val_hash(key) % h->ncells]; if( val_is_null(cmp) ) { while( c != NULL ) { if( val_compare(key,c->key) == 0 ) return val_true; c = c->next; } } else { while( c != NULL ) { if( val_call2(cmp,key,c->key) == alloc_int(0) ) return val_true; c = c->next; } } return val_false; } /** $hremove : 'hash -> k:any -> cmp:function:2? -> bool Look for the value bound to the key [k] in the hashtable. Use the comparison function [cmp] or [$compare] if [null]. Return true if such value exists and remove it from the hash, false either. **/ static value builtin_hremove( value vh, value key, value cmp ) { vhash *h; hcell *c, *prev = NULL; int hkey; if( !val_is_null(cmp) ) val_check_function(cmp,2); val_check_kind(vh,k_hash); h = val_hdata(vh); hkey = val_hash(key) % h->ncells; c = h->cells[hkey]; if( val_is_null(cmp) ) { while( c != NULL ) { if( val_compare(key,c->key) == 0 ) { if( prev == NULL ) h->cells[hkey] = c->next; else prev->next = c->next; h->nitems--; return val_true; } prev = c; c = c->next; } } else { while( c != NULL ) { if( val_call2(cmp,key,c->key) == alloc_int(0) ) { if( prev == NULL ) h->cells[hkey] = c->next; else prev->next = c->next; h->nitems--; return val_true; } prev = c; c = c->next; } } return val_false; } /** $hset : 'hash -> k:any -> v:any -> cmp:function:2? -> bool Set the value bound to key [k] to [v] or add it to the hashtable if not found. Return true if the value was added to the hashtable. **/ static value builtin_hset( value vh, value key, value val, value cmp ) { vhash *h; hcell *c; int hkey; if( !val_is_null(cmp) ) val_check_function(cmp,2); val_check_kind(vh,k_hash); h = val_hdata(vh); hkey = val_hash(key); c = h->cells[hkey % h->ncells]; if( val_is_null(cmp) ) { while( c != NULL ) { if( val_compare(key,c->key) == 0 ) { c->val = val; return val_false; } c = c->next; } } else { while( c != NULL ) { if( val_call2(cmp,key,c->key) == alloc_int(0) ) { c->val = val; return val_false; } c = c->next; } } if( h->nitems >= (h->ncells << 1) ) builtin_hresize(vh,alloc_int(h->ncells << 1)); c = (hcell*)alloc(sizeof(hcell)); c->hkey = hkey; c->key = key; c->val = val; hkey %= h->ncells; c->next = h->cells[hkey]; h->cells[hkey] = c; h->nitems++; return val_true; } /** $hadd : 'hash -> k:any -> v:any -> void Add the value [v] with key [k] to the hashtable. Previous binding is masked but not removed. **/ static value builtin_hadd( value vh, value key, value val ) { vhash *h; hcell *c; int hkey; val_check_kind(vh,k_hash); h = val_hdata(vh); hkey = val_hash(key); if( hkey < 0 ) neko_error(); if( h->nitems >= (h->ncells << 1) ) builtin_hresize(vh,alloc_int(h->ncells << 1)); c = (hcell*)alloc(sizeof(hcell)); c->hkey = hkey; c->key = key; c->val = val; hkey %= h->ncells; c->next = h->cells[hkey]; h->cells[hkey] = c; h->nitems++; return val_null; } /** $hiter : 'hash -> f:function:2 -> void Call the function [f] with every key and value in the hashtable **/ static value builtin_hiter( value vh, value f ) { int i; hcell *c; vhash *h; val_check_function(f,2); val_check_kind(vh,k_hash); h = val_hdata(vh); for(i=0;incells;i++) { c = h->cells[i]; while( c != NULL ) { val_call2(f,c->key,c->val); c = c->next; } } return val_null; } /** $hcount : 'hash -> int Return the number of elements in the hashtable **/ static value builtin_hcount( value vh ) { val_check_kind(vh,k_hash); return alloc_int( val_hdata(vh)->nitems ); } /** $hsize : 'hash -> int Return the size of the hashtable **/ static value builtin_hsize( value vh ) { val_check_kind(vh,k_hash); return alloc_int( val_hdata(vh)->ncells ); } /**

Other Builtins

**/ /** $print : any* -> void Can print any value **/ static value builtin_print( value *args, int nargs ) { buffer b; int i; if( nargs == 1 && val_is_string(*args) ) { val_print(*args); return neko_builtins[1]; } b = alloc_buffer(NULL); for(i=0;i any Throw any value as an exception. Never returns **/ static value builtin_throw( value v ) { val_throw(v); return val_null; } /** $rethrow : any -> any Throw any value as an exception while keeping previous exception stack. Never returns **/ static value builtin_rethrow( value v ) { val_rethrow(v); return val_null; } /** $istrue : v:any -> bool Return true if [v] is not [false], not [null] and not 0 **/ static value builtin_istrue( value f ) { return alloc_bool(f != val_false && f != val_null && f != alloc_int(0) && (val_is_int(f) || val_tag(f) != VAL_INT32 || val_int32(f) != 0)); } /** $not : any -> bool Return true if [v] is [false] or [null] or [0] **/ static value builtin_not( value f ) { return alloc_bool(f == val_false || f == val_null || f == alloc_int(0) || (!val_is_int(f) && val_tag(f) == VAL_INT32 && val_int32(f) == 0)); } /** $typeof : any -> int Return the type of a value. The following builtins are defined :
  • [$tnull] = 0
  • [$tint] = 1
  • [$tfloat] = 2
  • [$tbool] = 3
  • [$tstring] = 4
  • [$tobject] = 5
  • [$tarray] = 6
  • [$tfunction] = 7
  • [$tabstract] = 8
**/ static value builtin_typeof( value v ) { switch( val_type(v) ) { case VAL_INT: case VAL_INT32: return alloc_int(1); case VAL_NULL: return alloc_int(0); case VAL_FLOAT: return alloc_int(2); case VAL_BOOL: return alloc_int(3); case VAL_STRING: return alloc_int(4); case VAL_OBJECT: return alloc_int(5); case VAL_ARRAY: return alloc_int(6); case VAL_FUNCTION: return alloc_int(7); case VAL_ABSTRACT: return alloc_int(8); default: neko_error(); } } /** $compare : any -> any -> int? Compare two values and return 1, -1 or 0. Return [null] if comparison is not possible **/ static value builtin_compare( value a, value b ) { int r = val_compare(a,b); return (r == invalid_comparison)?val_null:alloc_int(r); } /** $pcompare : any -> any -> int Physically compare two values. Same as [$compare] for integers. **/ static value builtin_pcompare( value a, value b ) { int_val ia = (int_val)a; int_val ib = (int_val)b; if( ia > ib ) return alloc_int(1); else if( ia < ib ) return alloc_int(-1); else return alloc_int(0); } /** $excstack : void -> array Return the stack between the place the last exception was raised and the place it was catched. The stack is composed of the following items :
  • [null] when it's a C function
  • a string when it's a module without debug informations
  • an array of two elements (usually file and line) if debug informations where available
**/ static value builtin_excstack() { return NEKO_VM()->exc_stack; } /** $callstack : void -> array Return the current callstack. Same format as [$excstack] **/ static value builtin_callstack() { return neko_call_stack(NEKO_VM()); } /** $version : void -> int Return the version of Neko : 135 means 1.3.5 **/ static value builtin_version() { return alloc_int(NEKO_VERSION); } /** $neko_build_year : void -> int Return the year that Neko was compiled. **/ static value builtin_neko_build_year() { return alloc_int(NEKO_BUILD_YEAR); } /** $setresolver : function:2? -> void Set a function to callback with object and field id when an object field is not found. **/ static value builtin_setresolver( value f ) { neko_vm *vm = NEKO_VM(); if( val_is_null(f) ) vm->resolver = NULL; else { val_check_function(f,2); vm->resolver = f; } return val_null; } #define BUILTIN(name,nargs) \ alloc_field(neko_builtins[0],val_id(#name),alloc_function(builtin_##name,nargs,"$" #name)); void neko_init_builtins() { neko_builtins = alloc_root(2); neko_builtins[0] = alloc_object(NULL); neko_builtins[1] = alloc_function(builtin_print,VAR_ARGS,"$print"); BUILTIN(print,VAR_ARGS); BUILTIN(array,VAR_ARGS); BUILTIN(amake,1); BUILTIN(acopy,1); BUILTIN(asize,1); BUILTIN(asub,3); BUILTIN(ablit,5); BUILTIN(aconcat,1); BUILTIN(smake,1); BUILTIN(ssize,1); BUILTIN(scopy,1); BUILTIN(ssub,3); BUILTIN(sget,2); BUILTIN(sset,3); BUILTIN(sblit,5); BUILTIN(sfind,3); BUILTIN(sget16,3); BUILTIN(sget32,3); BUILTIN(sgetf,3); BUILTIN(sgetd,3); BUILTIN(sset16,4); BUILTIN(sset32,4); BUILTIN(ssetf,4); BUILTIN(ssetd,4); BUILTIN(itof,2); BUILTIN(itod,3); BUILTIN(ftoi,2); BUILTIN(dtoi,3); BUILTIN(isbigendian,0); BUILTIN(new,1); BUILTIN(objget,2); BUILTIN(objset,3); BUILTIN(objcall,3); BUILTIN(objfield,2); BUILTIN(objremove,2); BUILTIN(objfields,1); BUILTIN(hash,1); BUILTIN(fasthash,1); BUILTIN(field,1); BUILTIN(objsetproto,2); BUILTIN(objgetproto,1); BUILTIN(int,1); BUILTIN(float,1); BUILTIN(string,1); BUILTIN(typeof,1); BUILTIN(closure,VAR_ARGS); BUILTIN(apply,VAR_ARGS); BUILTIN(varargs,1); BUILTIN(compare,2); BUILTIN(pcompare,2); BUILTIN(not,1); BUILTIN(throw,1); BUILTIN(rethrow,1); BUILTIN(nargs,1); BUILTIN(call,3); BUILTIN(isnan,1); BUILTIN(isinfinite,1); BUILTIN(istrue,1); BUILTIN(getkind,1); BUILTIN(iskind,2); BUILTIN(hnew,1); BUILTIN(hget,3); BUILTIN(hmem,3); BUILTIN(hset,4); BUILTIN(hadd,3); BUILTIN(hremove,3); BUILTIN(hresize,2); BUILTIN(hkey,1); BUILTIN(hcount,1); BUILTIN(hsize,1); BUILTIN(hiter,2); BUILTIN(iadd,2); BUILTIN(isub,2); BUILTIN(imult,2); BUILTIN(idiv,2); BUILTIN(excstack,0); BUILTIN(callstack,0); BUILTIN(version,0); BUILTIN(neko_build_year,0); BUILTIN(setresolver,1); } /* ************************************************************************ */ neko-2-4-0/vm/callback.c000066400000000000000000000127651464615675700150310ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include "neko.h" #include "objtable.h" #include "vm.h" #include "neko_mod.h" #define MAXCALLS 350 typedef value (*c_prim0)(); typedef value (*c_prim1)(value); typedef value (*c_prim2)(value,value); typedef value (*c_prim3)(value,value,value); typedef value (*c_prim4)(value,value,value,value); typedef value (*c_prim5)(value,value,value,value,value); typedef value (*c_primN)(value*,int); typedef value (*jit_prim)( neko_vm *, void *, value, neko_module * ); extern void neko_setup_trap( neko_vm *vm ); extern void neko_process_trap( neko_vm *vm ); extern int neko_stack_expand( int_val *sp, int_val *csp, neko_vm *vm ); extern char *jit_boot_seq; EXTERN value val_callEx( value vthis, value f, value *args, int nargs, value *exc ) { neko_vm *vm = NEKO_VM(); value old_this = vm->vthis; value old_env = vm->env; value ret = val_null; jmp_buf oldjmp; if( vthis != NULL ) vm->vthis = vthis; if( exc ) { memcpy(&oldjmp,&vm->start,sizeof(jmp_buf)); if( setjmp(vm->start) ) { *exc = vm->vthis; neko_process_trap(vm); vm->vthis = old_this; vm->env = old_env; memcpy(&vm->start,&oldjmp,sizeof(jmp_buf)); return val_null; } neko_setup_trap(vm); } if( (uintptr_t)&vm < (uintptr_t)vm->c_stack_max ) val_throw(alloc_string("C Stack Overflow")); if( val_is_int(f) ) val_throw(alloc_string("Invalid call")); if( val_tag(f) == VAL_PRIMITIVE ) { vm->env = ((vfunction *)f)->env; if( nargs == ((vfunction*)f)->nargs ) { if( nargs > CALL_MAX_ARGS ) failure("Too many arguments for a call"); switch( nargs ) { case 0: ret = ((c_prim0)((vfunction*)f)->addr)(); break; case 1: ret = ((c_prim1)((vfunction*)f)->addr)(args[0]); break; case 2: ret = ((c_prim2)((vfunction*)f)->addr)(args[0],args[1]); break; case 3: ret = ((c_prim3)((vfunction*)f)->addr)(args[0],args[1],args[2]); break; case 4: ret = ((c_prim4)((vfunction*)f)->addr)(args[0],args[1],args[2],args[3]); break; case 5: ret = ((c_prim5)((vfunction*)f)->addr)(args[0],args[1],args[2],args[3],args[4]); break; } } else if( ((vfunction*)f)->nargs == -1 ) ret = (value)((c_primN)((vfunction*)f)->addr)(args,nargs); else val_throw(alloc_string("Invalid call")); if( ret == NULL ) val_throw( (value)((vfunction*)f)->module ); } else if( val_short_tag(f) == VAL_FUNCTION ) { if( nargs == ((vfunction*)f)->nargs ) { int n; if( vm->csp + 4 >= vm->sp - nargs && !neko_stack_expand(vm->sp,vm->csp,vm) ) { if( exc ) { neko_process_trap(vm); memcpy(&vm->start,&oldjmp,sizeof(jmp_buf)); } failure("Stack Overflow"); } else { for(n=0;nsp = (int_val)args[n]; vm->env = ((vfunction*)f)->env; if( val_tag(f) == VAL_FUNCTION ) { *++vm->csp = (int_val)callback_return; *++vm->csp = 0; *++vm->csp = 0; *++vm->csp = 0; ret = neko_interp(vm,((vfunction*)f)->module,(int_val)val_null,(int_val*)((vfunction*)f)->addr); } else { neko_module *m = (neko_module*)((vfunction*)f)->module; ret = ((jit_prim)jit_boot_seq)(vm,((vfunction*)f)->addr,val_null,m); } } } else val_throw(alloc_string("Invalid call")); } else val_throw(alloc_string("Invalid call")); if( exc ) { neko_process_trap(vm); memcpy(&vm->start,&oldjmp,sizeof(jmp_buf)); } vm->vthis = old_this; vm->env = old_env; return ret; } EXTERN value val_callN( value f, value *args, int nargs ) { return val_callEx(NULL,f,args,nargs,NULL); } EXTERN value val_ocallN( value o, field f, value *args, int nargs ) { return val_callEx(o,val_field(o,f),args,nargs,NULL); } EXTERN value val_call0( value f ) { return val_callN(f,NULL,0); } EXTERN value val_call1( value f, value v ) { return val_callN(f,&v,1); } EXTERN value val_call2( value f, value v1, value v2 ) { value args[2] = { v1, v2 }; return val_callN(f,args,2); } EXTERN value val_call3( value f, value arg1, value arg2, value arg3 ) { value args[3] = { arg1, arg2, arg3 }; return val_callN(f,args,3); } EXTERN value val_ocall0( value o, field f ) { return val_ocallN(o,f,NULL,0); } EXTERN value val_ocall1( value o, field f, value arg ) { return val_ocallN(o,f,&arg,1); } EXTERN value val_ocall2( value o, field f, value arg1, value arg2 ) { value args[2] = { arg1, arg2 }; return val_ocallN(o,f,args,2); } EXTERN value val_this() { return (value)NEKO_VM()->vthis; } /* ************************************************************************ */ neko-2-4-0/vm/elf.c000066400000000000000000000121111464615675700140240ustar00rootroot00000000000000/* * Copyright (C)2016-2017 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "neko_elf.h" /* None of this is needed on non-ELF platforms... */ #ifdef SEPARATE_SECTION_FOR_BYTECODE #include #include /* Name of neko bytecode section... */ static const char const* BYTECODE_SEC_NAME = ".nekobytecode"; /* Must be big enough to hold Elf32_Ehdr or Elf64_Ehdr... */ int size_Ehdr = sizeof(Elf64_Ehdr); /* Must be big enough to hold Elf32_Shdr or Elf64_Shdr... */ int size_Shdr = sizeof(Elf64_Shdr); static int is_32, shoff, shent, shnum, shstr; static char *strbuf; static int strsize, stroff; static value elf_read_exe(FILE *exe, int loc, char *buf, int size) { if ( 0 != fseek(exe,loc,SEEK_SET) || size != fread(buf,1,size,exe) ) { fclose(exe); return val_false; } return val_true; } static value elf_write_exe(FILE *exe, int loc, char *buf, int size) { if ( 0 != fseek(exe,loc,SEEK_SET) || size != fwrite(buf,1,size,exe) ) { fclose(exe); return val_false; } return val_true; } value elf_read_header(FILE *exe) { char hdr[size_Ehdr]; int hdrsize; /* First read the elf header to determine 32/64-bit-ness... */ if ( val_true != elf_read_exe(exe,0,hdr,EI_NIDENT) ) return val_false; if ( hdr[EI_CLASS] == ELFCLASS32 || hdr[EI_CLASS] == ELFCLASS64 ) { is_32 = hdr[EI_CLASS] == ELFCLASS32; } else return val_false; /* Read the full elf header now... */ hdrsize = is_32 ? sizeof(Elf32_Ehdr) : sizeof(Elf64_Ehdr); if ( val_true != elf_read_exe(exe,0,hdr,hdrsize) ) return val_false; if ( elf_get_Ehdr(hdr,e_type) != ET_EXEC ) return val_false; /* Remember the section headers info... */ shoff = elf_get_Ehdr(hdr,e_shoff); shent = elf_get_Ehdr(hdr,e_shentsize); shnum = elf_get_Ehdr(hdr,e_shnum); shstr = elf_get_Ehdr(hdr,e_shstrndx); return val_true; } int elf_is_32() { return is_32; } value elf_read_section(FILE *exe, int sec, char *buf) { return elf_read_exe(exe,shoff+sec*shent,buf,shent); } value elf_write_section(FILE *exe, int sec, char *buf) { return elf_write_exe(exe,shoff+sec*shent,buf,shent); } static value elf_read_section_string_table(FILE *exe) { char buf[size_Ehdr]; if ( NULL != strbuf ) return val_true; if ( val_true != elf_read_section(exe,shstr,buf) ) return val_false; stroff = elf_get_Shdr(buf,sh_offset); strsize = elf_get_Shdr(buf,sh_size); strbuf = (char*) malloc(strsize); if ( val_true != elf_read_exe(exe,stroff,strbuf,strsize) ) return val_false; return val_true; } void elf_free_section_string_table() { if ( NULL != strbuf ) { free(strbuf); strbuf = NULL; } } static int elf_find_section_by_name(FILE *exe, const char *name) { char buf[size_Shdr]; int shcur = 0, shname; if ( val_true != elf_read_section_string_table(exe) ) return -1; while ( shcur < shnum ) { if ( val_true != elf_read_section(exe,shcur,buf) ) return -1; shname = elf_get_Shdr(buf,sh_name); if ( shname < strsize && !strncmp(&strbuf[shname], name, strlen(name)) ) { /* found the .nekobytecode section! */ return shcur; } shcur++; } return -1; } int elf_find_bytecode_section(FILE *exe) { return elf_find_section_by_name(exe, BYTECODE_SEC_NAME); } value elf_find_embedded_bytecode(const char *file, int *beg, int *end) { FILE *exe; char buf[size_Shdr]; int bytecode_sec_idx; /* Open the file to update the elf nekobytecode section header... */ exe = fopen(file,"rb"); if( exe == NULL ) return val_false; /* First read the elf header... */ if ( val_true != elf_read_header(exe) ) goto failed; /* Find the right section header... */ bytecode_sec_idx = elf_find_bytecode_section(exe); if ( -1 == bytecode_sec_idx ) goto failed; if ( val_true != elf_read_section(exe,bytecode_sec_idx,buf) ) goto failed; elf_free_section_string_table(); fclose(exe); if ( NULL != beg ) *beg = elf_get_Shdr(buf,sh_offset); if ( NULL != end ) *end = elf_get_Shdr(buf,sh_offset) + elf_get_Shdr(buf,sh_size); return val_true; failed: elf_free_section_string_table(); fclose(exe); return val_false; } #endif neko-2-4-0/vm/hash.c000066400000000000000000000054461464615675700142160ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "neko.h" typedef struct vlist { value v; struct vlist *next; } vlist; typedef struct vparam { int *h; vlist l; } vparam; #define HBIG(x) *h = *h * 65599 + (x) #define HSMALL(x) *h = *h * 19 + (x) static void hash_obj_rec( value v, field f, void *_p ); static void hash_rec( value v, int *h, vlist *l ) { val_type t = val_type(v); switch( t ) { case VAL_INT: HBIG(val_int(v)); break; case VAL_INT32: HBIG(val_int32(v)); break; case VAL_NULL: HSMALL(0); break; case VAL_FLOAT: { int k = sizeof(tfloat); while( k ) HSMALL(val_string(v)[--k]); } break; case VAL_BOOL: HSMALL(val_bool(v)); break; case VAL_STRING: { int k = val_strlen(v); while( k ) HSMALL(val_string(v)[--k]); } break; case VAL_OBJECT: case VAL_ARRAY: { vlist *tmp = l; int k = 0; while( tmp != NULL ) { if( tmp->v == v ) { HSMALL(k); return; } k = k + 1; tmp = tmp->next; } } if( t == VAL_OBJECT ) { vparam p; p.h = h; p.l.v = v; p.l.next = l; val_iter_fields(v,hash_obj_rec,&p); v = (value)((vobject*)v)->proto; if( v != NULL ) hash_rec(v,h,&p.l); } else { vlist cur; int k = val_array_size(v); cur.v = v; cur.next = l; while( k ) hash_rec(val_array_ptr(v)[--k],h,&cur); } break; default: // ignore since we want hashes to be stable wrt memory break; } } static void hash_obj_rec( value v, field f, void *_p ) { vparam *p = (vparam*)_p; int *h = p->h; HBIG((int)f); hash_rec(v,h,&p->l); } EXTERN int val_hash( value v ) { int h = 0; hash_rec(v,&h,NULL); return (((unsigned int)h) & 0x3FFFFFFF); } /* ************************************************************************ */ neko-2-4-0/vm/interp.c000066400000000000000000000753421464615675700145760ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "opcodes.h" #include "vm.h" #include "neko_mod.h" #include "objtable.h" #ifndef NEKO_WINDOWS # include #endif #if defined(NEKO_GCC) && defined(NEKO_X86) && (__GNUC__ == 3) # define ACC_BACKUP int_val __acc = acc; # define ACC_RESTORE acc = __acc; # define ACC_REG asm("%eax") # define PC_REG asm("%esi") # define SP_REG asm("%edi") # define CSP_REG # define VM_ARG vm #elif defined(NEKO_GCC) && defined(NEKO_PPC) # define ACC_BACKUP # define ACC_RESTORE # define ACC_REG asm("26") # define PC_REG asm("27") # define SP_REG asm("28") # define CSP_REG asm("29") # define VM_REG asm("30") # define VM_ARG _vm #else # define ACC_BACKUP # define ACC_RESTORE # define ACC_REG # define PC_REG # define SP_REG # define CSP_REG # define VM_ARG vm #endif #define ERASE 0 #define address_int(a) (((int_val)(a)) | 1) #define int_address(a) (int_val*)(a & ~1) extern field id_add, id_radd, id_sub, id_rsub, id_mult, id_rmult, id_div, id_rdiv, id_mod, id_rmod; extern field id_get, id_set; extern value neko_alloc_module_function( void *m, int_val pos, int nargs ); extern char *jit_boot_seq; extern char *jit_handle_trap; typedef void (*jit_handle)( neko_vm * ); extern int neko_can_jit(); value NEKO_TYPEOF[] = { alloc_int(0), alloc_int(2), alloc_int(3), alloc_int(4), alloc_int(5), alloc_int(6), alloc_int(7), alloc_int(8), alloc_int(1), }; static void default_printer( const char *s, int len, void *out ) { while( len > 0 ) { int p = (int)fwrite(s,1,len,(FILE*)out); if( p <= 0 ) { fputs("[ABORTED]",(FILE*)out); break; } len -= p; s += p; } fflush((FILE*)out); } EXTERN neko_vm *neko_vm_alloc( void *custom ) { neko_vm *vm = (neko_vm*)alloc(sizeof(neko_vm)); # ifdef NEKO_WINDOWS int stack_size = 0x100000; // 1MB default # else struct rlimit st; int stack_size; if( getrlimit(RLIMIT_STACK,&st) != 0 || st.rlim_cur == RLIM_INFINITY ) stack_size = 8192 << 10; else stack_size = st.rlim_cur; # endif vm->spmin = (int_val*)alloc(INIT_STACK_SIZE*sizeof(int_val)); vm->print = default_printer; vm->print_param = stdout; vm->clist = NULL; // the maximum stack position for a C call is estimated // - stack grows bottom // - neko_vm_alloc should be near the beginning of the stack // - we keep 64KB for the C call work space and error margin vm->c_stack_max = (void*)(((int_val)&vm) - (stack_size - 0x10000)); vm->exc_stack = alloc_array(0); vm->spmax = vm->spmin + INIT_STACK_SIZE; vm->sp = vm->spmax; vm->csp = vm->spmin - 1; vm->vthis = val_null; vm->env = alloc_array(0); vm->jit_val = NULL; vm->run_jit = 0; vm->resolver = NULL; vm->trusted_code = 0; vm->fstats = NULL; vm->pstats = NULL; return vm; } EXTERN int neko_vm_jit( neko_vm *vm, int enable_jit ) { if( enable_jit < 0 ) return vm->run_jit; if( enable_jit ) vm->run_jit = neko_can_jit(); else vm->run_jit = 0; return vm->run_jit; } EXTERN int neko_vm_trusted( neko_vm *vm, int t ) { int old = vm->trusted_code; vm->trusted_code = t; return old; } EXTERN void neko_vm_set_stats( neko_vm *vm, neko_stat_func fstats, neko_stat_func pstats ) { vm->fstats = fstats; vm->pstats = pstats; } EXTERN void neko_vm_select( neko_vm *vm ) { local_set(neko_vm_context,vm); } EXTERN neko_vm *neko_vm_current() { return NEKO_VM(); } EXTERN void *neko_vm_custom( neko_vm *vm, vkind k ) { custom_list *c = vm->clist; while( c != NULL ) { if( c->tag == k ) return c->custom; c = c->next; } return NULL; } EXTERN void neko_vm_set_custom( neko_vm *vm, vkind k, void *v ) { custom_list *c = vm->clist, *prev = NULL; while( c != NULL ) { if( c->tag == k ) { if( v ) { c->custom = v; return; } if( prev == NULL ) vm->clist = c->next; else prev->next = c->next; return; } prev = c; c = c->next; } c = (custom_list*)alloc(sizeof(custom_list)); c->tag = k; c->custom = v; c->next = vm->clist; vm->clist = c; } typedef struct { neko_printer prev; void *prev_param; neko_printer cur; void *cur_param; } redirect_param; static void redirected_print( const char *s, int size, void *_p ) { redirect_param *p = (redirect_param*)_p; p->cur(s,size,p->cur_param); } EXTERN void neko_vm_redirect( neko_vm *vm, neko_printer print, void *param ) { redirect_param *p; if( print == NULL ) { if( vm->print != redirected_print ) return; p = (redirect_param*)vm->print_param; vm->print = p->prev; vm->print_param = p->prev_param; return; } p = (redirect_param*)alloc(sizeof(redirect_param)); p->prev = vm->print; p->prev_param = vm->print_param; p->cur = print; p->cur_param = param; vm->print = redirected_print; vm->print_param = p; } EXTERN value neko_vm_execute( neko_vm *vm, void *_m ) { unsigned int i; neko_module *m = (neko_module*)_m; value old_env = vm->env, ret; value old_this = vm->vthis; neko_vm_select(vm); for(i=0;infields;i++) val_id(val_string(m->fields[i])); vm->env = alloc_array(0); vm->vthis = val_null; ret = neko_interp(vm,m,(int_val)val_null,m->code); vm->env = old_env; vm->vthis = old_this; return ret; } EXTERN value neko_exc_stack( neko_vm *vm ) { return vm->exc_stack; } static value neko_flush_stack( int_val *cspup, int_val *csp, value old ); EXTERN value neko_call_stack( neko_vm *vm ) { return neko_flush_stack(vm->csp,vm->spmin - 1,NULL); } typedef int_val (*c_prim0)(); typedef int_val (*c_prim1)(int_val); typedef int_val (*c_prim2)(int_val,int_val); typedef int_val (*c_prim3)(int_val,int_val,int_val); typedef int_val (*c_prim4)(int_val,int_val,int_val,int_val); typedef int_val (*c_prim5)(int_val,int_val,int_val,int_val,int_val); typedef int_val (*c_primN)(value*,int); typedef int_val (*jit_prim)( neko_vm *, void *, value , neko_module *m ); static int_val jit_run( neko_vm *vm, vfunction *acc ) { neko_module *m = (neko_module*)acc->module; return ((jit_prim)jit_boot_seq)(vm,acc->addr,(value)acc,m); } #define RuntimeError(err,param) { if( param ) pc++; PushInfos(); BeginCall(); val_throw(alloc_string(err)); } #define CallFailure() RuntimeError("Invalid call",false) #define InvalidFieldAccess() { \ value v = val_field_name((field)*pc); \ buffer b; \ if( val_is_null(v) ) RuntimeError("Invalid field access",true); \ b = alloc_buffer("Invalid field access : "); \ val_buffer(b,v); \ pc++; PushInfos(); BeginCall(); \ val_throw(buffer_to_string(b)); \ } #ifdef NEKO_THREADED # define Instr(x) Label##x: # ifdef NEKO_DIRECT_THREADED # define Next goto *((const void *)*pc++); # else # define Next goto **(instructions + *pc++); # endif #else # define Instr(x) case x: # define Next break; #endif #define PopMacro(n) { \ int tmp = (int)n; \ while( tmp-- > 0 ) \ *sp++ = ERASE; \ } #define BeginCall() \ vm->sp = sp; \ vm->csp = csp; #define EndCall() \ sp = vm->sp; \ csp = vm->csp #define PushInfos() \ if( csp + 4 >= sp ) STACK_EXPAND; \ *++csp = (int_val)pc; \ *++csp = (int_val)vm->env; \ *++csp = (int_val)vm->vthis; \ *++csp = (int_val)m; #define PopInfos(restpc) \ m = (neko_module*)*csp; \ *csp-- = ERASE; \ vm->vthis = (value)*csp; \ *csp-- = ERASE; \ vm->env = (value)*csp; \ *csp-- = ERASE; \ if( restpc ) pc = (int_val*)*csp; \ *csp-- = ERASE; #define SetupBeforeCall(this_arg) \ vfunction *f = (vfunction*)acc; \ PushInfos(); \ vm->vthis = this_arg; \ vm->env = ((vfunction*)acc)->env; \ BeginCall(); #define RestoreAfterCall() \ if( acc == (int_val)NULL ) val_throw( (value)f->module ); \ EndCall(); \ PopInfos(false); #define DoCall(this_arg,pc_args) \ if( acc & 1 ) \ CallFailure() \ else if( val_tag(acc) == VAL_FUNCTION && pc_args == ((vfunction*)acc)->nargs ) { \ PushInfos(); \ m = (neko_module*)((vfunction*)acc)->module; \ pc = (int_val*)((vfunction*)acc)->addr; \ vm->vthis = this_arg; \ vm->env = ((vfunction*)acc)->env; \ } else if( val_tag(acc) == VAL_PRIMITIVE ) { \ if( pc_args == ((vfunction*)acc)->nargs ) { \ SetupBeforeCall(this_arg); \ switch( pc_args ) { \ case 0: \ acc = ((c_prim0)((vfunction*)acc)->addr)(); \ break; \ case 1: \ acc = ((c_prim1)((vfunction*)acc)->addr)(sp[0]); \ break; \ case 2: \ acc = ((c_prim2)((vfunction*)acc)->addr)(sp[1],sp[0]); \ break; \ case 3: \ acc = ((c_prim3)((vfunction*)acc)->addr)(sp[2],sp[1],sp[0]); \ break; \ case 4: \ acc = ((c_prim4)((vfunction*)acc)->addr)(sp[3],sp[2],sp[1],sp[0]); \ break; \ case 5: \ acc = ((c_prim5)((vfunction*)acc)->addr)(sp[4],sp[3],sp[2],sp[1],sp[0]); \ break; \ } \ RestoreAfterCall(); \ } \ else if( ((vfunction*)acc)->nargs == VAR_ARGS ) { \ int_val args[CALL_MAX_ARGS]; \ int_val tmp; \ SetupBeforeCall(this_arg); \ sp += pc_args; \ for(tmp=0;tmpaddr)((value*)(void*)args,(int)pc_args); \ RestoreAfterCall(); \ } else \ CallFailure(); \ PopMacro(pc_args); \ } else if( val_tag(acc) == VAL_JITFUN ) { \ if( pc_args == ((vfunction*)acc)->nargs ) { \ SetupBeforeCall(this_arg); \ acc = jit_run(vm,(vfunction*)acc); \ RestoreAfterCall(); \ } else \ CallFailure(); \ } else \ CallFailure(); #define OpError(op) RuntimeError("Invalid operation (" op ")", false) #define IntOp(op) \ if( (acc & 1) && (*sp & 1) ) \ acc = (int_val)alloc_best_int(val_int(*sp) op val_int(acc)); \ else if( val_is_any_int(acc) && val_is_any_int(*sp) ) \ acc = (int_val)alloc_best_int(val_any_int(*sp) op val_any_int(acc)); \ else \ OpError(#op); \ *sp++ = ERASE; \ Next #define Test(test) \ BeginCall(); \ acc = (int_val)val_compare((value)*sp,(value)acc); \ EndCall(); \ *sp++ = ERASE; \ acc = (int_val)((acc test 0 && acc != invalid_comparison)?val_true:val_false); \ Next #define SUB(x,y) ((x) - (y)) #define MULT(x,y) ((x) * (y)) #define DIV(x,y) ((x) / (y)) #define ObjectOpGen(obj,param,id,err) { \ ACC_BACKUP \ value _o = (value)obj; \ value _arg = (value)param; \ value _f = val_field(_o,id); \ if( _f == val_null ) { \ ACC_RESTORE \ err; \ } else { \ PushInfos(); \ BeginCall(); \ acc = (int_val)val_callEx(_o,_f,&_arg,1,NULL); \ EndCall(); \ PopInfos(false); \ } \ } #define ObjectOp(obj,param,id) ObjectOpGen(obj,param,id,RuntimeError("Unsupported operation",false)) #define NumberOp(op,fop,id_op,id_rop) \ if( (acc & 1) && (*sp & 1) ) \ acc = (int_val)alloc_best_int(val_int(*sp) op val_int(acc)); \ else if( acc & 1 ) { \ if( val_tag(*sp) == VAL_FLOAT ) \ acc = (int_val)alloc_float(fop(val_float(*sp),val_int(acc))); \ else if( val_tag(*sp) == VAL_INT32 ) \ acc = (int_val)alloc_best_int(val_int32(*sp) op val_int(acc)); \ else if( val_tag(*sp) == VAL_OBJECT ) \ ObjectOp(*sp,acc,id_op) \ else \ OpError(#op); \ } else if( *sp & 1 ) { \ if( val_tag(acc) == VAL_FLOAT ) \ acc = (int_val)alloc_float(fop(val_int(*sp),val_float(acc))); \ else if( val_tag(acc) == VAL_INT32 ) \ acc = (int_val)alloc_best_int(val_int(*sp) op val_int32(acc)); \ else if( val_tag(acc) == VAL_OBJECT ) \ ObjectOp(acc,*sp,id_rop) \ else \ OpError(#op); \ } else if( val_tag(acc) == VAL_FLOAT ) { \ if( val_tag(*sp) == VAL_FLOAT ) \ acc = (int_val)alloc_float(fop(val_float(*sp),val_float(acc))); \ else if( val_tag(*sp) == VAL_INT32 ) \ acc = (int_val)alloc_float(fop(val_int32(*sp),val_float(acc))); \ else \ goto id_op##_next; \ } else if( val_tag(acc) == VAL_INT32 ) {\ if( val_tag(*sp) == VAL_INT32 ) \ acc = (int_val)alloc_best_int(val_int32(*sp) op val_int32(acc)); \ else if( val_tag(*sp) == VAL_FLOAT ) \ acc = (int_val)alloc_float(fop(val_float(*sp),val_int32(acc))); \ else \ goto id_op##_next; \ } else { \ id_op##_next: \ if( val_tag(*sp) == VAL_OBJECT ) \ ObjectOpGen(*sp,acc,id_op,goto id_op##_next2) \ else { \ id_op##_next2: \ if( val_tag(acc) == VAL_OBJECT ) \ ObjectOp(acc,*sp,id_rop) \ else \ OpError(#op); \ } \ } \ *sp++ = ERASE; \ Next; extern int neko_stack_expand( int_val *sp, int_val *csp, neko_vm *vm ); extern value neko_append_int( neko_vm *vm, value str, int x, bool way ); extern value neko_append_strings( value s1, value s2 ); #define STACK_EXPAND { \ ACC_BACKUP; \ if( neko_stack_expand(sp,csp,vm) ) { \ sp = vm->sp; \ csp = vm->csp; \ } else \ val_throw(alloc_string("Stack Overflow")); \ ACC_RESTORE; \ } // optimized for sparse bits static int bitcount( unsigned int k ) { int b = 0; while( k ) { b++; k &= (k - 1); } return b; } static value neko_flush_stack( int_val *cspup, int_val *csp, value old ) { int ncalls = (int)((cspup - csp) / 4); value stack_trace = alloc_array(ncalls + ((old == NULL)?0:val_array_size(old))); value *st = val_array_ptr(stack_trace); neko_module *m; while( csp != cspup ) { m = (neko_module*)csp[4]; if( m ) { if( m->dbgidxs ) { unsigned int ppc = (unsigned int)((((int_val**)csp)[1]-2) - m->code); if( ppc < m->codesize ) { int idx =m->dbgidxs[ppc>>5].base + bitcount(m->dbgidxs[ppc>>5].bits >> (31 - (ppc & 31))); *st = val_array_ptr(m->dbgtbl)[idx]; } else *st = m->name; } else *st = m->name; } else *st = val_null; st++; if( old ) { *++csp = ERASE; *++csp = ERASE; *++csp = ERASE; *++csp = ERASE; } else csp += 4; } if( old ) { value *oldst = val_array_ptr(old); ncalls = val_array_size(old); while( ncalls-- ) *st++ = *oldst++; } return stack_trace; } EXTERN void neko_vm_dump_stack( neko_vm *vm ) { // we can't do any GC allocation here since we might hold the lock int_val *cspup = vm->csp; int_val *csp = vm->spmin - 1; while( csp != cspup ) { neko_module *m = (neko_module*)csp[4]; printf("Called from "); if( m ) { printf("%s ",val_string(m->name)); if( m->dbgidxs ) { int ppc = (int)((((int_val**)csp)[1]-2) - m->code); int idx = m->dbgidxs[ppc>>5].base + bitcount(m->dbgidxs[ppc>>5].bits >> (31 - (ppc & 31))); value s = val_array_ptr(m->dbgtbl)[idx]; if( val_is_string(s) ) printf("%s",val_string(s)); else if( val_is_array(s) && val_array_size(s) == 2 && val_is_string(val_array_ptr(s)[0]) && val_is_int(val_array_ptr(s)[1]) ) printf("file %s line %d",val_string(val_array_ptr(s)[0]),val_int(val_array_ptr(s)[1])); else printf("???"); } } else printf("a C function"); csp += 4; printf("\n"); } fflush(stdout); } void neko_setup_trap( neko_vm *vm ) { vm->sp -= 6; if( vm->sp <= vm->csp && !neko_stack_expand(vm->sp,vm->csp,vm) ) val_throw(alloc_string("Stack Overflow")); vm->sp[0] = (int_val)alloc_int((int_val)(vm->csp - vm->spmin)); vm->sp[1] = (int_val)vm->vthis; vm->sp[2] = (int_val)vm->env; vm->sp[3] = address_int(vm->jit_val); vm->sp[4] = (int_val)val_null; vm->sp[5] = (int_val)alloc_int((int_val)vm->trap); vm->trap = vm->spmax - vm->sp; } void neko_process_trap( neko_vm *vm ) { // pop csp int_val *sp; int_val *trap; if( vm->trap == 0 ) return; trap = vm->spmax - vm->trap; sp = vm->spmin + val_int(trap[0]); vm->exc_stack = neko_flush_stack(vm->csp,sp,vm->exc_stack); vm->csp = sp; // restore state vm->vthis = (value)trap[1]; vm->env = (value)trap[2]; vm->jit_val = int_address(trap[3]); // pop sp sp = trap + 6; vm->trap = val_int(trap[5]); while( vm->sp < sp ) *vm->sp++ = ERASE; } int_val neko_interp_loop( neko_vm *VM_ARG, neko_module *m, int_val _acc, int_val *_pc ) { register int_val acc ACC_REG = _acc; register int_val *pc PC_REG = _pc; # ifdef VM_REG register neko_vm *vm VM_REG = VM_ARG; # endif # ifdef NEKO_THREADED static void *instructions[] = { # undef _NEKO_OPCODES_H # undef OPBEGIN # undef OPEND # undef OP # define OPBEGIN # define OPEND # define OP(x) &&Label##x # include "opcodes.h" }; if( m == NULL ) return (int_val)instructions; # endif register int_val *sp SP_REG = vm->sp; register int_val *csp CSP_REG = vm->csp; #ifdef NEKO_THREADED Next; {{ #else while( true ) { # ifdef NEKO_PROF if( *pc != Last ) pc[PROF_SIZE]++; # endif switch( *pc++ ) { #endif Instr(AccNull) acc = (int_val)val_null; Next; Instr(AccTrue) acc = (int_val)val_true; Next; Instr(AccFalse) acc = (int_val)val_false; Next; Instr(AccThis) acc = (int_val)vm->vthis; Next; Instr(AccInt) acc = *pc++; Next; Instr(AccInt32) acc = (int_val)alloc_int32((int)*pc++); Next; Instr(AccStack0) acc = *sp; Next; Instr(AccStack1) acc = sp[1]; Next; Instr(AccStack) acc = sp[*pc++]; Next; Instr(AccGlobal) acc = *(int_val*)(*pc++); Next; Instr(AccEnv) if( *pc >= val_array_size(vm->env) ) RuntimeError("Reading Outside Env",true); acc = (int_val)val_array_ptr(vm->env)[*pc++]; Next; Instr(AccField) if( val_is_object(acc) ) { value *f; value old = (value)acc, tacc = (value)acc; do { f = otable_find(&((vobject*)acc)->table,(field)*pc); if( f ) break; acc = (int_val)((vobject*)tacc)->proto; tacc = (value)acc; } while( acc ); if( f ) acc = (int_val)*f; else if( vm->resolver ) { BeginCall(); acc = (int_val)val_call2(vm->resolver,old,alloc_int(*pc)); EndCall(); } else acc = (int_val)val_null; } else InvalidFieldAccess(); pc++; Next; Instr(AccArray) if( val_is_int(acc) && val_is_array(*sp) ) { int k = val_int(acc); if( k < 0 || k >= val_array_size(*sp) ) acc = (int_val)val_null; else acc = (int_val)val_array_ptr(*sp)[k]; } else if( val_is_object(*sp) ) ObjectOp(*sp,acc,id_get) else if( val_is_int32(acc) && val_is_array(*sp) ) acc = (int_val)val_null; else RuntimeError("Invalid array access",false); *sp++ = ERASE; Next; Instr(AccIndex0) if( val_is_array(acc) ) { if( val_array_size(acc) ) acc = (int_val)*val_array_ptr(acc); else acc = (int_val)val_null; } else if( val_is_object(acc) ) ObjectOp(acc,alloc_int(0),id_get) else RuntimeError("Invalid array access",false); Next; Instr(AccIndex1) if( val_is_array(acc) ) { if( val_array_size(acc) > 1 ) acc = (int_val)val_array_ptr(acc)[1]; else acc = (int_val)val_null; } else if( val_is_object(acc) ) ObjectOp(acc,alloc_int(1),id_get) else RuntimeError("Invalid array access",false); Next; Instr(AccIndex) if( val_is_array(acc) ) { if( *pc < 0 || *pc >= val_array_size(acc) ) acc = (int_val)val_null; else acc = (int_val)val_array_ptr(acc)[*pc]; pc++; } else if( val_is_object(acc) ) ObjectOp(acc,alloc_int(*pc++),id_get) else RuntimeError("Invalid array access",true); Next; Instr(AccBuiltin) acc = *pc++; Next; Instr(SetStack) sp[*pc++] = acc; Next; Instr(SetGlobal) *(int_val*)(*pc++) = acc; Next; Instr(SetEnv) if( *pc >= val_array_size(vm->env) ) RuntimeError("Writing Outside Env",true); val_array_ptr(vm->env)[*pc++] = (value)acc; Next; Instr(SetField) if( val_is_object(*sp) ) { ACC_BACKUP; otable_replace(&((vobject*)*sp)->table,(field)*pc,(value)acc); ACC_RESTORE; } else InvalidFieldAccess(); *sp++ = ERASE; pc++; Next; Instr(SetArray) if( val_is_array(*sp) && val_is_int(sp[1]) ) { int k = val_int(sp[1]); if( k >= 0 && k < val_array_size(*sp) ) val_array_ptr(*sp)[k] = (value)acc; } else if( val_is_object(*sp) ) { value args[] = { (value)sp[1], (value)acc }; value f = val_field((value)*sp,id_set); if( f == val_null ) RuntimeError("Unsupported operation",false); PushInfos(); BeginCall(); val_callEx((value)*sp,f,args,2,NULL); EndCall(); PopInfos(false); acc = (int_val)args[1]; } else if( val_is_int32(sp[1]) && val_is_array(*sp) ) { // out range } else RuntimeError("Invalid array access",false); *sp++ = ERASE; *sp++ = ERASE; Next; Instr(SetIndex) if( val_is_array(*sp) ) { if( *pc >= 0 && *pc < val_array_size(*sp) ) val_array_ptr(*sp)[*pc] = (value)acc; } else if( val_is_object(*sp) ) { value args[] = { (value)alloc_int(*pc), (value)acc }; value f = val_field((value)*sp,id_set); if( f == val_null ) RuntimeError("Unsupported operation",true); PushInfos(); BeginCall(); val_callEx((value)*sp,f,args,2,NULL); EndCall(); PopInfos(false); acc = (int_val)args[1]; } else RuntimeError("Invalid array access",true); pc++; *sp++ = ERASE; Next; Instr(SetThis) vm->vthis = (value)acc; Next; Instr(Push) --sp; if( sp <= csp ) STACK_EXPAND; *sp = acc; Next; Instr(Pop) PopMacro(*pc++) Next; Instr(Apply) if( !val_is_function(acc) ) RuntimeError("$apply",true); { int fargs = val_fun_nargs(acc); if( fargs == *pc || fargs == VAR_ARGS ) goto do_call; if( *pc > fargs ) RuntimeError("$apply",true); { int i = fargs; ACC_BACKUP value env = alloc_array(fargs + 1); ACC_RESTORE; val_array_ptr(env)[0] = (value)acc; while( i > *pc ) val_array_ptr(env)[i--] = val_null; while( i ) { val_array_ptr(env)[i--] = (value)*sp; *sp++ = ERASE; } acc = (int_val)neko_alloc_apply((int)(fargs - *pc++),env); } } Next; Instr(TailCall) { int stack = (int)((*pc) >> 3); int nargs = (int)((*pc) & 7); int i = nargs; value cur_this = vm->vthis; stack -= nargs; sp += nargs; while( i > 0 ) { sp--; sp[stack] = *sp; i--; } while( stack-- > 0 ) *sp++ = ERASE; // preserve 'this' through the call PopInfos(true); DoCall(cur_this,nargs); } Next; Instr(Call) do_call: pc++; DoCall(vm->vthis,pc[-1]); Next; Instr(ObjCall) { value vtmp = (value)*sp; *sp++ = ERASE; pc++; DoCall(vtmp,pc[-1]); } Next; Instr(Jump) pc = (int_val*)*pc; Next; Instr(JumpIf) if( acc == (int_val)val_true ) pc = (int_val*)*pc; else pc++; Next; Instr(JumpIfNot) if( acc != (int_val)val_true ) pc = (int_val*)*pc; else pc++; Next; Instr(Trap) sp -= 6; if( sp <= csp ) STACK_EXPAND; sp[0] = (int_val)alloc_int((int_val)(csp - vm->spmin)); sp[1] = (int_val)vm->vthis; sp[2] = (int_val)vm->env; sp[3] = address_int(*pc); sp[4] = address_int(m); sp[5] = (int_val)alloc_int(vm->trap); vm->trap = vm->spmax - sp; pc++; Next; Instr(EndTrap) if( vm->spmax - vm->trap != sp ) RuntimeError("Invalid End Trap",false); vm->trap = val_int(sp[5]); PopMacro(6); Next; Instr(Ret) PopMacro( *pc++ ); PopInfos(true); Next; Instr(MakeEnv) { int n = (int)(*pc++); ACC_BACKUP int_val tmp = (int_val)alloc_array(n); ACC_RESTORE; while( n-- ) { val_array_ptr(tmp)[n] = (value)*sp; *sp++ = ERASE; } if( val_is_int(acc) || val_tag(acc) != VAL_FUNCTION ) RuntimeError("Invalid environment",false); acc = (int_val)neko_alloc_module_function(((vfunction*)acc)->module,(int_val)((vfunction*)acc)->addr,((vfunction*)acc)->nargs); ((vfunction*)acc)->env = (value)tmp; } Next; Instr(MakeArray) { int n = (int)*pc++; ACC_BACKUP value arr = alloc_array(n+1); ACC_RESTORE; while( n ) { val_array_ptr(arr)[n] = (value)*sp; *sp++ = ERASE; n--; } val_array_ptr(arr)[0] = (value)acc; acc = (int_val)arr; } Next; Instr(MakeArray2) { // similar to MakeArray but will keep a correct evaluation order int n = (int)*pc++; ACC_BACKUP value arr = alloc_array(n+1); ACC_RESTORE; if( n == 2 ) { n += 0; } val_array_ptr(arr)[n] = (value)acc; while( n ) { val_array_ptr(arr)[--n] = (value)*sp; *sp++ = ERASE; } acc = (int_val)arr; } Next; Instr(Bool) acc = (acc == (int_val)val_false || acc == (int_val)val_null || acc == 1)?(int_val)val_false:(int_val)val_true; Next; Instr(Not) acc = (acc == (int_val)val_false || acc == (int_val)val_null || acc == 1)?(int_val)val_true:(int_val)val_false; Next; Instr(IsNull) acc = (int_val)((acc == (int_val)val_null)?val_true:val_false); Next; Instr(IsNotNull) acc = (int_val)((acc == (int_val)val_null)?val_false:val_true); Next; Instr(Add) if( (acc & 1) && (*sp & 1) ) acc = (int_val)alloc_best_int(val_int(*sp) + val_int(acc)); else if( acc & 1 ) { if( val_tag(*sp) == VAL_FLOAT ) acc = (int_val)alloc_float(val_float(*sp) + val_int(acc)); else if( val_tag(*sp) == VAL_INT32 ) acc = (int_val)alloc_best_int(val_int32(*sp) + val_int(acc)); else if( val_short_tag(*sp) == VAL_STRING ) acc = (int_val)neko_append_int(vm,(value)*sp,val_int(acc),true); else if( val_tag(*sp) == VAL_OBJECT ) ObjectOp(*sp,acc,id_add) else OpError("+"); } else if( *sp & 1 ) { if( val_tag(acc) == VAL_FLOAT ) acc = (int_val)alloc_float(val_int(*sp) + val_float(acc)); else if( val_tag(acc) == VAL_INT32 ) acc = (int_val)alloc_best_int(val_int(*sp) + val_int32(acc)); else if( val_short_tag(acc) == VAL_STRING ) acc = (int_val)neko_append_int(vm,(value)acc,val_int(*sp),false); else if( val_tag(acc) == VAL_OBJECT ) ObjectOp(acc,*sp,id_radd) else OpError("+"); } else if( val_tag(acc) == VAL_FLOAT ) { if( val_tag(*sp) == VAL_FLOAT ) acc = (int_val)alloc_float(val_float(*sp) + val_float(acc)); else if( val_tag(*sp) == VAL_INT32 ) acc = (int_val)alloc_float(val_int32(*sp) + val_float(acc)); else goto add_next; } else if( val_tag(acc) == VAL_INT32 ) { if( val_tag(*sp) == VAL_INT32 ) acc = (int_val)alloc_best_int(val_int32(*sp) + val_int32(acc)); else if( val_tag(*sp) == VAL_FLOAT ) acc = (int_val)alloc_float(val_float(*sp) + val_int32(acc)); else goto add_next; } else { add_next: if( val_tag(*sp) == VAL_OBJECT ) ObjectOpGen(*sp,acc,id_add,goto add_2) else { add_2: if( val_tag(acc) == VAL_OBJECT ) ObjectOpGen(acc,*sp,id_radd,goto add_3) else { add_3: if( val_short_tag(acc) == VAL_STRING || val_short_tag(*sp) == VAL_STRING ) { ACC_BACKUP buffer b = alloc_buffer(NULL); BeginCall(); val_buffer(b,(value)*sp); ACC_RESTORE; val_buffer(b,(value)acc); EndCall(); acc = (int_val)buffer_to_string(b); } else OpError("+"); } } } *sp++ = ERASE; Next; Instr(Sub) NumberOp(-,SUB,id_sub,id_rsub) Instr(Mult) NumberOp(*,MULT,id_mult,id_rmult) Instr(Div) if( val_is_number(acc) && val_is_number(*sp) ) acc = (int_val)alloc_float( ((tfloat)val_number(*sp)) / val_number(acc) ); else if( val_is_object(*sp) ) ObjectOpGen(*sp,acc,id_div,goto div_next) else { div_next: if( val_is_object(acc) ) ObjectOp(acc,*sp,id_rdiv) else OpError("/"); } *sp++ = ERASE; Next; Instr(Mod) if( (acc == 1 || (val_is_int32(acc) && val_int32(acc)==0)) && val_is_any_int(*sp) ) OpError("%"); NumberOp(%,fmod,id_mod,id_rmod); Instr(Shl) IntOp(<<); Instr(Shr) IntOp(>>); Instr(UShr) if( (acc & 1) && (*sp & 1) ) acc = (int_val)alloc_best_int(((unsigned int)val_int(*sp)) >> val_int(acc)); else if( val_is_any_int(acc) && val_is_any_int(*sp) ) acc = (int_val)alloc_best_int(((unsigned int)val_any_int(*sp)) >> val_any_int(acc)); else OpError(">>>"); *sp++ = ERASE; Next; Instr(Or) IntOp(|); Instr(And) IntOp(&); Instr(Xor) IntOp(^); Instr(Eq) Test(==) Instr(Neq) BeginCall(); acc = (int_val)((val_compare((value)*sp,(value)acc) == 0)?val_false:val_true); EndCall(); *sp++ = ERASE; Next; Instr(Lt) Test(<) Instr(Lte) Test(<=) Instr(Gt) Test(>) Instr(Gte) Test(>=) Instr(TypeOf) acc = (int_val)(val_is_int(acc) ? alloc_int(1) : NEKO_TYPEOF[val_short_tag(acc)]); Next; Instr(Compare) BeginCall(); acc = (int_val)val_compare((value)*sp,(value)acc); EndCall(); acc = (int_val)((acc == invalid_comparison)?val_null:alloc_int(acc)); *sp++ = ERASE; Next; Instr(PhysCompare) acc = (int_val)(( *sp > acc )?alloc_int(1):(( *sp < acc )?alloc_int(-1):alloc_int(0))); *sp++ = ERASE; Next; Instr(Hash) if( val_is_string(acc) ) { BeginCall(); acc = (int_val)alloc_int( val_id(val_string(acc)) ); } else RuntimeError("$hash",false); Next; Instr(New) BeginCall(); acc = (int_val)alloc_object((value)acc); Next; Instr(JumpTable) if( val_is_int(acc) && ((unsigned)acc) < ((unsigned)*pc) ) pc += acc; else pc += *pc + 1; Next; Instr(Loop) // space for GC/Debug Next; Instr(Last) goto end; #ifdef NEKO_VCC default: __assume(0); #endif }} end: vm->sp = sp; vm->csp = csp; return acc; } int_val *neko_get_ttable() { # ifdef NEKO_THREADED return (int_val*)neko_interp_loop(NULL,NULL,0,NULL); # else return NULL; # endif } value neko_interp( neko_vm *vm, void *_m, int_val acc, int_val *pc ) { int_val *sp, *csp, *trap; int_val init_sp = vm->spmax - vm->sp; neko_module *m = (neko_module*)_m; jmp_buf old; memcpy(&old,&vm->start,sizeof(jmp_buf)); if( setjmp(vm->start) ) { acc = (int_val)vm->vthis; // if uncaught or outside init stack, reraise if( vm->trap == 0 || vm->trap <= init_sp ) { char **tmp; memcpy(&vm->start,&old,sizeof(jmp_buf)); tmp = (char**)vm->start; if( *tmp == jit_handle_trap ) ((jit_handle)jit_handle_trap)(vm); else longjmp(vm->start,1); } trap = vm->spmax - vm->trap; if( trap < vm->sp ) { // trap outside stack vm->trap = 0; val_throw(alloc_string("Invalid Trap")); } // pop csp csp = vm->spmin + val_int(trap[0]); vm->exc_stack = neko_flush_stack(vm->csp,csp,vm->exc_stack); vm->csp = csp; // restore state vm->vthis = (value)trap[1]; vm->env = (value)trap[2]; pc = int_address(trap[3]); m = (neko_module*)int_address(trap[4]); // pop sp sp = trap + 6; vm->trap = val_int(trap[5]); while( vm->sp < sp ) *vm->sp++ = ERASE; // jit return ? if( val_is_kind(m,neko_kind_module) ) { m = (neko_module*)val_data(m); pc = (int_val*)((((int_val)pc)>>1) + (int_val)m->jit); acc = ((jit_prim)jit_boot_seq)(vm,pc,(value)acc,m); return (value)acc; } } if( m->jit != NULL && m->code == pc ) acc = ((jit_prim)jit_boot_seq)(vm,m->jit,(value)acc,m); else acc = neko_interp_loop(vm,m,acc,pc); memcpy(&vm->start,&old,sizeof(jmp_buf)); return (value)acc; } /* ************************************************************************ */ neko-2-4-0/vm/jit_x86.c000066400000000000000000001676651464615675700146020ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "vm.h" #include "neko_mod.h" #include "objtable.h" #include #include #include #include #ifdef NEKO_POSIX # include # include # define USE_MMAP #endif #define tmp_alloc(size) malloc(size) #define tmp_free(ptr) free(ptr) #ifdef NEKO_MAC #define STACK_ALIGN #endif #if defined(NEKO_WINDOWS) && defined(_DEBUG_XX) #define STACK_ALIGN #define STACK_ALIGN_DEBUG #endif #define TAG_MASK ((1<pos); tmp_free(ctx->baseptr); failure("JIT error"); } #define CONST(v) ((int)(int_val)(v)) #define PATCH_JUMP(local) if( local != NULL ) { \ int delta = (int)((int_val)buf.p - ((int_val)local + 1)); \ if( sizeof(*local) == sizeof(int) ) \ *local = delta - 3; \ else { \ if( delta > 127 || delta < -127 ) \ ERROR; \ *local = (char)delta; \ } \ } \ #define FIELD(n) ((n) * 4) #define VMFIELD(f) ((int)(int_val)&((neko_vm*)0)->f) #define FUNFIELD(f) ((int)(int_val)&((vfunction*)0)->f) #define POS() ((int)((int_val)ctx->buf.p - (int_val)ctx->baseptr)) #define GET_PC() CONST(ctx->module->code + ctx->curpc) #define INIT_BUFFER register jit_buffer buf = ctx->buf #define END_BUFFER ctx->buf = buf #define MOD_RM(mod,reg,rm) B((mod << 6) | (reg << 3) | rm) #define SIB MOD_RM #define IS_SBYTE(c) ( (c) >= -128 && (c) < 128 ) #define OP_RM(op,mod,reg,rm) { B(op); MOD_RM(mod,reg,rm); } #define OP_ADDR(op,addr,reg,rm) { B(op); \ MOD_RM(((addr) == 0 && reg != Ebp)?0:(IS_SBYTE(addr)?1:2),rm,reg); \ if( reg == Esp ) B(0x24); \ if( (addr) == 0 && reg != Ebp ) {} \ else if IS_SBYTE(addr) B(addr); \ else W(addr); } // OPCODES : // _r : register // _c : constant // _b : 8-bit constant // _a : [constant] // _i : [reg] // _p : [reg + constant:idx] // _x : [reg + reg:idx * mult] #define XRet() B(0xC3) #define XMov_rr(dst,src) OP_RM(0x8B,3,dst,src) #define XMov_rc(dst,cst) B(0xB8+(dst)); W(cst) #define XMov_rp(dst,reg,idx) OP_ADDR(0x8B,idx,reg,dst) #define XMov_ra(dst,addr) OP_RM(0x8B,0,dst,5); W(addr) #define XMov_rx(dst,r,idx,mult) OP_RM(0x8B,0,dst,4); SIB(Mult##mult,idx,r) #define XMov_pr(dst,idx,src) OP_ADDR(0x89,idx,dst,src) #define XMov_pc(dst,idx,c) OP_ADDR(0xC7,idx,dst,0); W(c) #define XMov_ar(addr,reg) B(0x3E); if( reg == Eax ) { B(0xA3); } else { OP_RM(0x89,0,reg,5); }; W(addr) #define XMov_xr(r,idx,mult,src) OP_RM(0x89,0,src,4); SIB(Mult##mult,idx,r) #define XCall_r(r) OP_RM(0xFF,3,2,r) #define XCall_m_debug(v) { \ int *l; \ XMov_rr(TMP,Ebp); \ XSub_rr(TMP,Esp); \ XAnd_rc(TMP,15); \ XCmp_rc(TMP,0); \ XJump(JEq,l); \ XShr_rc(TMP,2); \ XPush_r(TMP); \ XPush_c(CONST(__LINE__)); \ XMov_rc(TMP,CONST(debug_method_call)); \ XCall_r(TMP); \ PATCH_JUMP(l); \ XMov_rc(TMP,CONST(v)); \ XCall_r(TMP); \ } #define XCall_m_real(v) XMov_rc(TMP,CONST(v)); XCall_r(TMP); #ifdef STACK_ALIGN_DEBUG # define XCall_m XCall_m_debug #else # define XCall_m XCall_m_real #endif #define XCall_d(delta) B(0xE8); W(delta) #define XPush_r(r) B(0x50+(r)) #define XPush_c(cst) B(0x68); W(cst) #define XPush_p(reg,idx) OP_ADDR(0xFF,idx,reg,6) #define XAdd_rc(reg,cst) if IS_SBYTE(cst) { OP_RM(0x83,3,0,reg); B(cst); } else { OP_RM(0x81,3,0,reg); W(cst); } #define XAdd_rr(dst,src) OP_RM(0x03,3,dst,src) #define XSub_rc(reg,cst) if IS_SBYTE(cst) { OP_RM(0x83,3,5,reg); B(cst); } else { OP_RM(0x81,3,5,reg); W(cst); } #define XSub_rr(dst,src) OP_RM(0x2B,3,dst,src) #define XCmp_rr(r1,r2) OP_RM(0x3B,3,r1,r2) #define XCmp_rc(reg,cst) if( reg == Eax ) { B(0x3D); } else { OP_RM(0x81,3,7,reg); }; W(cst) #define XCmp_rb(reg,byte) OP_RM(0x83,3,7,reg); B(byte) #define XJump(how,local) if( (how) == JAlways ) { B(0xE9); } else { B(0x0F); B(how); }; local = buf.i; W(0) #define XJump_near(local) B(0xEB); local = buf.c; B(0) #define XJump_r(reg) OP_RM(0xFF,3,4,reg) #define XPop_r(reg) B(0x58 + (reg)) #define XTest_rc(r,cst) if( r == Eax ) { B(0xA9); W(cst); } else { B(0xF7); MOD_RM(3,0,r); W(cst); } #define XTest_rr(r,src) B(0x85); MOD_RM(3,r,src) #define XAnd_rc(r,cst) if( r == Eax ) { B(0x25); W(cst); } else { B(0x81); MOD_RM(3,4,r); W(cst); } #define XAnd_rr(r,src) B(0x23); MOD_RM(3,r,src) #define XOr_rc(r,cst) if( r == Eax ) { B(0x0D); W(cst); } else { B(0x81); MOD_RM(3,1,r); W(cst); } #define XOr_rr(r,src) B(0x0B); MOD_RM(3,r,src) #define XXor_rc(r,cst) if( r == Eax ) { B(0x35); W(cst); } else { B(0x81); MOD_RM(3,6,r); W(cst); } #define XXor_rr(r,src) B(0x33); MOD_RM(3,r,src) #define shift_r(r,spec) B(0xD3); MOD_RM(3,spec,r); #define shift_c(r,n,spec) if( (n) == 1 ) { B(0xD1); MOD_RM(3,spec,r); } else { B(0xC1); MOD_RM(3,spec,r); B(n); } #define XShl_rr(r,src) if( src != Ecx ) ERROR; shift_r(r,4) #define XShl_rc(r,n) shift_c(r,n,4) #define XShr_rr(r,src) if( src != Ecx ) ERROR; shift_r(r,7) #define XShr_rc(r,n) shift_c(r,n,7) #define XUShr_rr(r,src) if( src != Ecx ) ERROR; shift_r(r,5) #define XUShr_rc(r,n) shift_c(r,n,5) #define XIMul_rr(dst,src) B(0x0F); B(0xAF); MOD_RM(3,dst,src) #define XIDiv_r(r) B(0xF7); MOD_RM(3,7,r) #define XCdq() B(0x99); // FPU #define XFAddp() B(0xDE); B(0xC1) #define XFSubp() B(0xDE); B(0xE9) #define XFMulp() B(0xDE); B(0xC9) #define XFDivp() B(0xDE); B(0xF9) #define XFStp_i(r) B(0xDD); MOD_RM(0,3,r); if( r == Esp ) B(0x24) #define XFLd_i(r) B(0xDD); MOD_RM(0,0,r); if( r == Esp ) B(0x24) #define XFILd_i(r) B(0xDB); MOD_RM(0,0,r); if( r == Esp ) B(0x24) #define is_int(r,flag,local) { XTest_rc(r,1); XJump((flag)?JNeq:JEq,local); } #ifdef STACK_ALIGN # define stack_pad(n) stack_push(Esp,n) # define stack_pop_pad(n,n2) stack_pop(Esp,((n) + (n2))) #else # define stack_pad(n) # define stack_pop_pad(n,n2) stack_pop(Esp,(n)) #endif #define stack_push(r,n) \ if( (n) != 0 ) { \ if( (r) == CSP ) { \ XAdd_rc(r,(n) * 4); \ } else { \ XSub_rc(r,(n) * 4); \ } \ } #define stack_pop(r,n) \ if( (n) != 0 ) { \ if( (r) == CSP ) { \ XSub_rc(r,(n) * 4); \ } else { \ XAdd_rc(r,(n) * 4); \ } \ } #define begin_call() { XMov_pr(VM,VMFIELD(sp),SP); XMov_pr(VM,VMFIELD(csp),CSP); } #define end_call() { XMov_rp(SP,VM,VMFIELD(sp)); XMov_rp(CSP,VM,VMFIELD(csp)); } #define label(code) { XMov_rc(TMP2,CONST(code)); XCall_r(TMP2); } #define todo(str) { int *loop; XMov_rc(TMP2,CONST(str)); XJump(JAlways,loop); *loop = -5; } #define pop(n) if( (n) != 0 ) { \ int i = (n); \ while( i-- > 0 ) { \ XMov_pc(SP,FIELD(i),0); \ } \ stack_pop(SP,n); \ } #define pop_loop(n) { \ char *start; \ int *loop; \ XMov_rc(TMP,n); \ start = buf.c; \ XMov_pc(SP,FIELD(0),0); \ stack_pop(SP,1); \ XSub_rc(TMP,1); \ XCmp_rc(TMP,0); \ XJump(JNeq,loop); \ *loop = (int)(start - buf.c); \ } #ifdef STACK_ALIGN # define PAD_OPT(x) (x) #else # define PAD_OPT(x) 0 #endif #define runtime_error(msg_id,in_label) { \ if( in_label ) { stack_pad(2); } else { stack_pad(1); } \ XPush_c(CONST(strings[msg_id])); \ if( in_label ) { \ XMov_rp(TMP2,Esp,FIELD(2+PAD_OPT(2))); \ XPush_r(TMP2); \ } else { \ XPush_c(GET_PC()); \ } \ label(code->runtime_error); \ } #define get_var_r(reg,v) { \ switch( v ) { \ case VThis: \ XMov_rp(reg,VM,VMFIELD(vthis)); \ break; \ case VEnv: \ XMov_rp(reg,VM,VMFIELD(env)); \ break; \ case VModule: \ XMov_rp(reg,VM,VMFIELD(jit_val)); \ break; \ case VVm: \ XMov_rr(reg,VM); \ break; \ case VSpMax: \ XMov_rp(reg,VM,VMFIELD(spmax)); \ break; \ case VTrap: \ XMov_rp(reg,VM,VMFIELD(trap)); \ break; \ default: \ ERROR; \ break; \ } \ } #define get_var_p(reg,idx,v) { \ switch( v ) { \ case VThis: \ XMov_rp(TMP,VM,VMFIELD(vthis)); \ XMov_pr(reg,idx,TMP); \ break; \ case VEnv: \ XMov_rp(TMP,VM,VMFIELD(env)); \ XMov_pr(reg,idx,TMP); \ break; \ case VModule: \ XMov_rp(TMP,VM,VMFIELD(jit_val)); \ XMov_pr(reg,idx,TMP); \ break; \ case VVm: \ XMov_pr(reg,idx,VM); \ break; \ case VSpMax: \ XMov_rp(TMP,VM,VMFIELD(spmax)); \ XMov_pr(reg,idx,TMP); \ break; \ case VTrap: \ XMov_rp(TMP,VM,VMFIELD(trap)); \ XMov_pr(reg,idx,TMP); \ break; \ default: \ ERROR; \ break; \ } \ } #define set_var_r(v,reg) { \ switch( v ) { \ case VThis: \ XMov_pr(VM,VMFIELD(vthis),reg); \ break; \ case VEnv: \ XMov_pr(VM,VMFIELD(env),reg); \ break; \ case VTrap: \ XMov_pr(VM,VMFIELD(trap),reg); \ break; \ case VModule: \ XMov_pr(VM,VMFIELD(jit_val),reg); \ break; \ default: \ ERROR; \ break; \ } \ } #define set_var_p(v,reg,idx) { \ switch( v ) { \ case VThis: \ XMov_rp(TMP,reg,idx); \ XMov_pr(VM,VMFIELD(vthis),TMP); \ break; \ case VEnv: \ XMov_rp(TMP,reg,idx); \ XMov_pr(VM,VMFIELD(env),TMP); \ break; \ case VTrap: \ XMov_rp(TMP,reg,idx); \ XMov_pr(VM,VMFIELD(trap),TMP); \ break; \ case VModule: \ XMov_rp(TMP,reg,idx); \ XMov_pr(VM,VMFIELD(jit_val),TMP); \ break; \ default: \ ERROR; \ break; \ } \ } #define jump(how,targ) { \ jlist *j = (jlist*)alloc(sizeof(jlist)); \ void *jcode; \ j->target = (int)((int_val*)(int_val)(targ) - ctx->module->code); \ j->next = ctx->jumps; \ ctx->jumps = j; \ XJump(how,jcode); \ j->pos = (int)((int_val)jcode - (int_val)ctx->baseptr); \ } #define setup_before_call(mode,is_callb) { \ push_infos(is_callb?CALLBACK:PC_ARG); \ if( !is_callb ) { XPush_r(ACC); } \ if( mode == THIS_CALL ) { \ set_var_p(VThis,SP,FIELD(0)); \ pop(1); \ } \ set_var_p(VEnv,ACC,FUNFIELD(env)); \ } #define restore_after_call(nargs,pad) { \ int *jok; \ XCmp_rc(ACC,0); \ XJump(JNeq,jok); \ XMov_rp(ACC,Esp,FIELD(nargs+PAD_OPT(pad))); \ XMov_rp(ACC,ACC,FUNFIELD(module)); \ stack_pad(-1); \ XPush_r(ACC); \ XCall_m(val_throw); \ PATCH_JUMP(jok); \ stack_pop_pad(1+nargs,pad); \ pop_infos(); \ } #define NARGS (CALL_MAX_ARGS + 1) #define MAX_ENV 8 typedef struct { char *boot; char *stack_expand; char *runtime_error; char *call_normal_jit[NARGS]; char *call_this_jit[NARGS]; char *call_tail_jit[NARGS]; char *call_normal_prim[NARGS]; char *call_this_prim[NARGS]; char *call_tail_prim[NARGS]; char *call_normal_fun[NARGS]; char *call_this_fun[NARGS]; char *call_tail_fun[NARGS]; char *make_env[MAX_ENV]; char *make_env_n; char *oo_get; char *oo_set; char *handle_trap; char *invalid_access; } jit_code; char *jit_boot_seq = NULL; char *jit_handle_trap = NULL; static jit_code *code; static value *strings; static const char *cstrings[] = { "Stack overflow", // 0 "Reading Outside Env", // 1 "Writing Outside Env", // 2 "Invalid call", // 3 "Invalid array access", // 4 "Invalid field access", // 5 "Invalid environment", // 6 "Invalid operation (%)", // 7 "$apply", // 8 "Invalid End Trap", // 9 "$hash", // 10 }; #define DEFINE_PROC(p,arg) ctx->buf = buf; jit_##p(ctx,arg); buf = ctx->buf #define push_infos(arg) DEFINE_PROC(push_infos,arg) #define test(arg) DEFINE_PROC(test,arg) #define call(mode,nargs) ctx->buf = buf; jit_call(ctx,mode,nargs); buf = ctx->buf #define number_op(arg) DEFINE_PROC(number_op,arg) #define array_access(p) DEFINE_PROC(array_access,p) #define int_op(arg) DEFINE_PROC(int_op,arg) #define best_int() DEFINE_PROC(best_int,0) #ifdef STACK_ALIGN_DEBUG #include static void debug_method_call( int line, int stack ) { printf("Stack align error line %d (%d)\n" , line ,stack); exit(-1); } #endif #ifdef NEKO_JIT_DEBUG static void val_print_2( value v ) { val_print(alloc_string(" ")); val_print(v); } static void val_print_3( value v ) { val_print_2(v); val_print(alloc_string("\n")); } #endif static jit_ctx *jit_init_context( void *ptr, int size ) { jit_ctx *c = (jit_ctx*)alloc(sizeof(jit_ctx)); c->size = size; c->baseptr = ptr; c->buf.p = ptr; c->pos = NULL; c->curpc = 0; c->debug_wait = 0; c->jumps = NULL; c->traps = NULL; return c; } static void jit_finalize_context( jit_ctx *ctx ) { jlist *l; int nbytes = POS(); if( nbytes == 0 || nbytes > ctx->size ) *(int*)0xAABBCC = 0; l = ctx->jumps; while( l != NULL ) { *(int*)((char*)ctx->baseptr + l->pos) = ctx->pos[l->target] - (l->pos + 4); l = l->next; } l = ctx->traps; while( l != NULL ) { *(int*)((char*)ctx->baseptr + l->pos) = ctx->pos[l->target] + (int)(int_val)ctx->baseptr; l = l->next; } } static void jit_push_infos( jit_ctx *ctx, enum PushInfosMode callb ) { INIT_BUFFER; stack_push(CSP,4); if( callb == CALLBACK ) { XMov_pc(CSP,FIELD(-3),CONST(callback_return)); get_var_p(CSP,FIELD(-2),VEnv); get_var_p(CSP,FIELD(-1),VThis); XMov_pc(CSP,FIELD(0),0); } else { if( callb == PC_CUR ) { XMov_pc(CSP,FIELD(-3),GET_PC()); } else { // PC_ARG : on the stack XMov_rp(TMP2,Esp,FIELD(1)); XMov_pr(CSP,FIELD(-3),TMP2); } get_var_p(CSP,FIELD(-2),VEnv); get_var_p(CSP,FIELD(-1),VThis); get_var_p(CSP,FIELD(0),VModule) } END_BUFFER; } static void jit_best_int( jit_ctx *ctx, int _ ) { int *wrap; char *jend; INIT_BUFFER; XMov_rr(TMP,ACC); XShl_rc(ACC,1); XJump(JOverflow,wrap); XOr_rc(ACC,1); XJump_near(jend); PATCH_JUMP(wrap); XPush_r(TMP); XCall_m(alloc_int32); stack_pop(Esp,1); PATCH_JUMP(jend); END_BUFFER; } #define pop_infos() { \ set_var_p(VModule,CSP,FIELD(0)); \ set_var_p(VThis,CSP,FIELD(-1)); \ set_var_p(VEnv,CSP,FIELD(-2)); \ XMov_pc(CSP,FIELD(0),0); \ XMov_pc(CSP,FIELD(-1),0); \ XMov_pc(CSP,FIELD(-2),0); \ XMov_pc(CSP,FIELD(-3),0); \ stack_pop(CSP,4); \ } static void jit_boot( jit_ctx *ctx, void *_ ) { INIT_BUFFER; XPush_r(Ebp); XPush_r(Ebx); XPush_r(Esi); # ifdef STACK_ALIGN_DEBUG XMov_rr(Ebp,Esp); // ALIGNED STACK # endif XPush_r(Edi); XMov_rp(VM,Esp,FIELD(5)); get_var_r(TMP,VModule); XPush_r(TMP); set_var_p(VModule,Esp,FIELD(9)); XMov_rp(TMP,Esp,FIELD(7)); XMov_rp(ACC,Esp,FIELD(8)); end_call(); XCall_r(TMP); begin_call(); XPop_r(TMP); set_var_r(VModule,TMP); XPop_r(Edi); XPop_r(Esi); XPop_r(Ebx); XPop_r(Ebp); XRet(); END_BUFFER; } static void jit_trap( jit_ctx *ctx, int n ) { INIT_BUFFER; XMov_rp(VM,Esp,FIELD(1)); get_var_r(Ebp,VThis); // restore vm stack_pad(3); XPush_r(VM); XCall_m(neko_process_trap); stack_pop_pad(1,3); // restore registers end_call(); XMov_rr(ACC,Ebp); XMov_rp(Ebp,VM,VMFIELD(start)+FIELD(1)); XMov_rp(Esp,VM,VMFIELD(start)+FIELD(2)); XMov_rp(TMP2,VM,VMFIELD(start)+FIELD(3)); // restore vm jmp_buf XPop_r(TMP); XMov_pr(VM,VMFIELD(start)+FIELD(3),TMP); XPop_r(TMP); XMov_pr(VM,VMFIELD(start)+FIELD(2),TMP); XPop_r(TMP); XMov_pr(VM,VMFIELD(start)+FIELD(1),TMP); XPop_r(TMP); XMov_pr(VM,VMFIELD(start),TMP); XPush_r(TMP2); XRet(); END_BUFFER; } static void jit_stack_expand( jit_ctx *ctx, int _ ) { int *jresize, *jdone; int max = MAX_STACK_PER_FUNCTION; INIT_BUFFER; stack_push(CSP,max); XCmp_rr(SP,CSP); XJump(JLt,jresize); stack_pop(CSP,max); XRet(); PATCH_JUMP(jresize); stack_pop(CSP,max); XPush_r(ACC); XPush_r(VM); XPush_r(CSP); XPush_r(SP); XCall_m(neko_stack_expand); XCmp_rb(ACC,0); XJump(JNeq,jdone); stack_pad(-1); XPush_c(CONST(strings[0])); // Stack overflow XCall_m(val_throw); PATCH_JUMP(jdone); XMov_rp(ACC,Esp,FIELD(3)); end_call(); stack_pop(Esp,4); XRet(); END_BUFFER; } static void jit_runtime_error( jit_ctx *ctx, void *unused ) { INIT_BUFFER; push_infos(PC_ARG); // pc begin_call(); XMov_rp(TMP,Esp,FIELD(2)); // msg on stack XPush_r(TMP); XCall_m(val_throw); END_BUFFER; } static void jit_invalid_access( jit_ctx *ctx, int _ ) { INIT_BUFFER; int *jnext; // if( val_field_name(f) == val_null ) RuntimeError("Invalid field access") stack_pad(1); XMov_rp(TMP,Esp,FIELD(2)); // field XPush_r(TMP); XCall_m(val_field_name); stack_pop_pad(1,1); XCmp_rc(ACC,CONST(val_null)); XJump(JNeq,jnext); runtime_error(5,true); // else { // b = alloc_buffer("Invalid field access : "); PATCH_JUMP(jnext); XPush_r(ACC); XPush_c(CONST("Invalid field access : ")); XCall_m(alloc_buffer); stack_pop(Esp,1); // val_buffer(b,v); XPush_r(ACC); XCall_m(val_buffer); // buffer_to_string(b); XCall_m(buffer_to_string); stack_pop(Esp,2); push_infos(PC_ARG); // pc begin_call(); XPush_r(ACC); XCall_m(val_throw); END_BUFFER; } static void jit_test( jit_ctx *ctx, int how ) { INIT_BUFFER; int *jnot1, *jnot2; char *jend; // call val_compare(sp[0],acc) XPush_r(ACC); XMov_rp(TMP,SP,FIELD(0)); XPush_r(TMP); begin_call(); XCall_m(val_compare); end_call(); stack_pop(Esp,2); pop(1); // test ok and != invalid_comparison XCmp_rc(ACC,0); XJump(how,jnot1); XCmp_rc(ACC,invalid_comparison); XJump(JEq,jnot2); XMov_rc(ACC,CONST(val_true)); XJump_near(jend); PATCH_JUMP(jnot1); PATCH_JUMP(jnot2); XMov_rc(ACC,CONST(val_false)); PATCH_JUMP(jend); END_BUFFER; } static void jit_call( jit_ctx *ctx, int mode, int nargs ) { INIT_BUFFER; int *jerr, *jother, *jerr2; char *jend1, *jend2, *jend3; // if( is_int ) : error is_int(ACC,1,jerr); // if( type == jit ) XMov_rp(TMP,ACC,FUNFIELD(t)); XCmp_rb(TMP,VAL_JITFUN); XJump(JNeq,jother); XPush_c(GET_PC()); switch( mode ) { case NORMAL: label(code->call_normal_jit[nargs]); break; case THIS_CALL: label(code->call_this_jit[nargs]); break; case TAIL_CALL: label(code->call_tail_jit[nargs]); break; } if( mode == TAIL_CALL ) jend1 = NULL; else { XJump_near(jend1); } // else if( type == prim ) PATCH_JUMP(jother); XCmp_rb(TMP,VAL_PRIMITIVE); XJump(JNeq,jother); XPush_c(GET_PC()); switch( mode ) { case NORMAL: label(code->call_normal_prim[nargs]); break; case THIS_CALL: label(code->call_this_prim[nargs]); break; case TAIL_CALL: label(code->call_tail_prim[nargs]); break; } XJump_near(jend2); // else if( type == function ) PATCH_JUMP(jother); XCmp_rb(TMP,VAL_FUNCTION); XJump(JNeq,jerr2); XPush_c(GET_PC()); switch( mode ) { case NORMAL: label(code->call_normal_fun[nargs]); break; case THIS_CALL: label(code->call_this_fun[nargs]); break; case TAIL_CALL: label(code->call_tail_fun[nargs]); break; } XJump_near(jend3); // else error PATCH_JUMP(jerr); PATCH_JUMP(jerr2); runtime_error(3,false); // Invalid call // end PATCH_JUMP(jend1); PATCH_JUMP(jend2); PATCH_JUMP(jend3); stack_pop(Esp,1); // pushed pc END_BUFFER; } static void jit_call_jit( jit_ctx *ctx, int nargs, int mode ) { INIT_BUFFER; int *jerr; // check arg count XMov_rp(TMP,ACC,FUNFIELD(nargs)); XCmp_rb(TMP,nargs); XJump(JNeq,jerr); if( mode == TAIL_CALL ) { // pop PC and EIP from the stack stack_pop(Esp,2); set_var_p(VModule,ACC,FUNFIELD(module)); set_var_p(VEnv,ACC,FUNFIELD(env)); XMov_rp(TMP,ACC,FUNFIELD(addr)); XJump_r(TMP); } else { push_infos(PC_ARG); set_var_p(VModule,ACC,FUNFIELD(module)); set_var_p(VEnv,ACC,FUNFIELD(env)); if( mode == THIS_CALL ) { set_var_p(VThis,SP,FIELD(0)); pop(1); } XMov_rp(TMP,ACC,FUNFIELD(addr)); stack_pad(1); XCall_r(TMP); stack_pad(-1); pop_infos(); XRet(); } PATCH_JUMP(jerr); runtime_error(3,true); // Invalid call END_BUFFER; } static void jit_call_prim( jit_ctx *ctx, int nargs, int mode ) { INIT_BUFFER; int *jvararg, *jerr; int i; # ifdef STACK_ALIGN int pad_size = 4 - ((2+nargs)%4); # endif // check arg count XMov_rp(TMP,ACC,FUNFIELD(nargs)); XCmp_rb(TMP,nargs); XJump(JNeq,jvararg); // push args from VMSP to PROCSP setup_before_call(mode,false); stack_pad(pad_size); for(i=0;isp,vm->csp,vm) ) val_throw(alloc_string("Stack Overflow")) #define ERASE 0 #define ACC_BACKUP #define ACC_RESTORE #define PushInfos() \ if( vm->csp + 4 >= vm->sp ) STACK_EXPAND; \ *++vm->csp = (int_val)pc; \ *++vm->csp = (int_val)vm->env; \ *++vm->csp = (int_val)vm->vthis; \ *++vm->csp = (int_val)vm->jit_val; #define PopInfos(restpc) \ vm->jit_val = (void*)*vm->csp; \ *vm->csp-- = ERASE; \ vm->vthis = (value)*vm->csp; \ *vm->csp-- = ERASE; \ vm->env = (value)*vm->csp; \ *vm->csp-- = ERASE; \ if( restpc ) pc = (int)*vm->csp; \ *vm->csp-- = ERASE; #define BeginCall() #define EndCall() #define RuntimeError(err) { PushInfos(); BeginCall(); val_throw(alloc_string(err)); } #define ObjectOpGen(obj,param,id,err) { \ value _o = (value)obj; \ value _arg = (value)param; \ value _f = val_field(_o,id); \ if( _f == val_null ) { \ err; \ } else { \ PushInfos(); \ BeginCall(); \ acc = (int_val)val_callEx(_o,_f,&_arg,1,NULL); \ EndCall(); \ PopInfos(false); \ } \ } #define ObjectOp(obj,param,id) ObjectOpGen(obj,param,id,RuntimeError("Unsupported operation")) #define OpError(op) RuntimeError("Invalid operation (" op ")") static int_val generic_add( neko_vm *vm, int_val acc, int_val sp, int pc ) { if( acc & 1 ) { if( val_tag(sp) == VAL_FLOAT ) acc = (int_val)alloc_float(val_float(sp) + val_int(acc)); else if( val_tag(sp) == VAL_INT32 ) acc = (int_val)alloc_best_int(val_int32(sp) + val_int(acc)); else if( val_short_tag(sp) == VAL_STRING ) acc = (int_val)neko_append_int(vm,(value)sp,val_int(acc),true); else if( val_tag(sp) == VAL_OBJECT ) ObjectOp(sp,acc,id_add) else OpError("+"); } else if( sp & 1 ) { if( val_tag(acc) == VAL_FLOAT ) acc = (int_val)alloc_float(val_int(sp) + val_float(acc)); else if( val_tag(acc) == VAL_INT32 ) acc = (int_val)alloc_best_int(val_int(sp) + val_int32(acc)); else if( val_short_tag(acc) == VAL_STRING ) acc = (int_val)neko_append_int(vm,(value)acc,val_int(sp),false); else if( val_tag(acc) == VAL_OBJECT ) ObjectOp(acc,sp,id_radd) else OpError("+"); } else if( val_tag(acc) == VAL_FLOAT ) { if( val_tag(sp) == VAL_FLOAT ) acc = (int_val)alloc_float(val_float(sp) + val_float(acc)); else if( val_tag(sp) == VAL_INT32 ) acc = (int_val)alloc_float(val_int32(sp) + val_float(acc)); else goto add_next; } else if( val_tag(acc) == VAL_INT32 ) { if( val_tag(sp) == VAL_INT32 ) acc = (int_val)alloc_best_int(val_int32(sp) + val_int32(acc)); else if( val_tag(sp) == VAL_FLOAT ) acc = (int_val)alloc_float(val_float(sp) + val_int32(acc)); else goto add_next; } else { add_next: if( val_tag(sp) == VAL_OBJECT ) ObjectOpGen(sp,acc,id_add,goto add_2) else { add_2: if( val_tag(acc) == VAL_OBJECT ) ObjectOpGen(acc,sp,id_radd,goto add_3) else { add_3: if( val_short_tag(acc) == VAL_STRING || val_short_tag(sp) == VAL_STRING ) { ACC_BACKUP buffer b = alloc_buffer(NULL); BeginCall(); val_buffer(b,(value)sp); ACC_RESTORE; val_buffer(b,(value)acc); EndCall(); acc = (int_val)buffer_to_string(b); } else OpError("+"); } } } return acc; } #define NumberOp(op,fop,id_op,id_rop) \ if( acc & 1 ) { \ if( val_tag(sp) == VAL_FLOAT ) \ acc = (int_val)alloc_float(fop(val_float(sp),val_int(acc))); \ else if( val_tag(sp) == VAL_INT32 ) \ acc = (int_val)alloc_best_int(val_int32(sp) op val_int(acc)); \ else if( val_tag(sp) == VAL_OBJECT ) \ ObjectOp(sp,acc,id_op) \ else \ OpError(#op); \ } else if( sp & 1 ) { \ if( val_tag(acc) == VAL_FLOAT ) \ acc = (int_val)alloc_float(fop(val_int(sp),val_float(acc))); \ else if( val_tag(acc) == VAL_INT32 ) \ acc = (int_val)alloc_best_int(val_int(sp) op val_int32(acc)); \ else if( val_tag(acc) == VAL_OBJECT ) \ ObjectOp(acc,sp,id_rop) \ else \ OpError(#op); \ } else if( val_tag(acc) == VAL_FLOAT ) { \ if( val_tag(sp) == VAL_FLOAT ) \ acc = (int_val)alloc_float(fop(val_float(sp),val_float(acc))); \ else if( val_tag(sp) == VAL_INT32 ) \ acc = (int_val)alloc_float(fop(val_int32(sp),val_float(acc))); \ else \ goto id_op##_next; \ } else if( val_tag(acc) == VAL_INT32 ) {\ if( val_tag(sp) == VAL_INT32 ) \ acc = (int_val)alloc_best_int(val_int32(sp) op val_int32(acc)); \ else if( val_tag(sp) == VAL_FLOAT ) \ acc = (int_val)alloc_float(fop(val_float(sp),val_int32(acc))); \ else \ goto id_op##_next; \ } else { \ id_op##_next: \ if( val_tag(sp) == VAL_OBJECT ) \ ObjectOpGen(sp,acc,id_op,goto id_op##_next2) \ else { \ id_op##_next2: \ if( val_tag(acc) == VAL_OBJECT ) \ ObjectOp(acc,sp,id_rop) \ else \ OpError(#op); \ } \ } #define SUB(x,y) ((x) - (y)) #define MULT(x,y) ((x) * (y)) #define DIV(x,y) ((x) / (y)) #define GENERIC_OP(id,op,fop) \ static int_val generic_##id( neko_vm *vm, int_val acc, int_val sp, int pc ) { \ NumberOp(op,fop,id_##id,id_r##id); \ return acc; \ } GENERIC_OP(sub,-,SUB); GENERIC_OP(mult,*,MULT); static int_val generic_div( neko_vm *vm, int_val acc, int_val sp, int pc ) { if( val_is_number(acc) && val_is_number(sp) ) acc = (int_val)alloc_float( ((tfloat)val_number(sp)) / val_number(acc) ); else if( val_is_object(sp) ) ObjectOpGen(sp,acc,id_div,goto div_next) else { div_next: if( val_is_object(acc) ) ObjectOp(acc,sp,id_rdiv) else OpError("/"); } return acc; } static int_val generic_mod( neko_vm *vm, int_val acc, int_val sp, int pc ) { if( (acc == 1 || (val_is_int32(acc) && val_int32(acc)==0)) && val_is_any_int(sp) ) OpError("%"); NumberOp(%,fmod,id_mod,id_rmod); return acc; } #define GENERIC_IOP(id,op) \ static int_val generic_##id( neko_vm *vm, int_val acc, int_val sp, int pc ) { \ if( val_is_any_int(acc) && val_is_any_int(sp) ) \ acc = (int_val)alloc_best_int(val_any_int(sp) op val_any_int(acc)); \ else \ OpError(#op); \ return acc; \ } GENERIC_IOP(shl,<<); GENERIC_IOP(shr,>>); GENERIC_IOP(or,|); GENERIC_IOP(and,&); GENERIC_IOP(xor,^); static int_val generic_ushr( neko_vm *vm, int_val acc, int_val sp, int pc ) { if( val_is_any_int(acc) && val_is_any_int(sp) ) acc = (int_val)alloc_best_int( ((unsigned int)val_any_int(sp)) >> val_any_int(acc)); else OpError(">>>"); return acc; } // -------------------------------------------- // we only inline operations for (int,int) and (float,float) // other cases are handled by a single generic_op primitive // through a small jit_generic_* wrapper static void jit_number_op( jit_ctx *ctx, enum Operation op ) { INIT_BUFFER; int *jnot_int, *jnot_int2, *jint, *jnot_float1, *jnot_float2, *jmod0; char *jend, *jend2, *jdiv = NULL; // tmp = acc XMov_rr(TMP,ACC); // acc = *sp XMov_rp(ACC,SP,FIELD(0)); // is_int(acc) && is_int(sp) is_int(ACC,false,jnot_int); is_int(TMP,false,jnot_int2); XShr_rc(ACC,1); XShr_rc(TMP,1); switch( op ) { case OP_ADD: XAdd_rr(ACC,TMP); break; case OP_SUB: XSub_rr(ACC,TMP); break; case OP_MUL: XIMul_rr(ACC,TMP); break; case OP_MOD: XCmp_rc(TMP,0); XJump(JNeq,jmod0); runtime_error(7,false); PATCH_JUMP(jmod0); XCdq(); XIDiv_r(TMP); XMov_rr(ACC,Edx); break; case OP_DIV: XPush_r(ACC); XFILd_i(Esp); XPush_r(TMP); XFILd_i(Esp); stack_pop(Esp,2); XJump_near(jdiv); break; default: ERROR; break; } best_int(); XJump_near(jend); // is_float(acc) && is_float(sp) PATCH_JUMP(jnot_int); XMov_rp(TMP2,ACC,FIELD(0)); XCmp_rb(TMP2,VAL_FLOAT); XJump(JNeq,jnot_float1); is_int(TMP,true,jint); XMov_rp(TMP2,TMP,FIELD(0)); XCmp_rb(TMP2,VAL_FLOAT); XJump(JNeq,jnot_float2); // load floats XAdd_rc(ACC,4); XFLd_i(ACC); XAdd_rc(TMP,4); XFLd_i(TMP); switch( op ) { case OP_ADD: XFAddp(); break; case OP_SUB: XFSubp(); break; case OP_DIV: PATCH_JUMP(jdiv); XFDivp(); break; case OP_MUL: XFMulp(); break; case OP_MOD: stack_push(Esp,2); XFStp_i(Esp); stack_push(Esp,2); XFStp_i(Esp); XCall_m(fmod); stack_pop(Esp,2); break; default: ERROR; break; } if( op != OP_MOD ) { stack_push(Esp,2); } XFStp_i(Esp); XCall_m(alloc_float); stack_pop(Esp,2); XJump_near(jend2); // else... PATCH_JUMP(jint); PATCH_JUMP(jnot_float1); PATCH_JUMP(jnot_float2); PATCH_JUMP(jnot_int2); begin_call(); XPush_c(GET_PC()); XPush_r(ACC); XPush_r(TMP); XPush_r(VM); switch( op ) { case OP_ADD: XCall_m(generic_add); break; case OP_SUB: XCall_m(generic_sub); break; case OP_DIV: XCall_m(generic_div); break; case OP_MUL: XCall_m(generic_mult); break; case OP_MOD: XCall_m(generic_mod); break; case OP_GET: case OP_SET: case OP_LAST: // not used here break; } stack_pop(Esp,4); end_call(); PATCH_JUMP(jend); PATCH_JUMP(jend2); pop(1); END_BUFFER; } static void jit_int_op( jit_ctx *ctx, enum IOperation op ) { INIT_BUFFER; int *jerr1, *jerr2; char *jend; XMov_rr(TMP,ACC); XMov_rp(ACC,SP,FIELD(0)); is_int(ACC,false,jerr1); is_int(TMP,false,jerr2); XShr_rc(TMP,1); XShr_rc(ACC,1); switch( op ) { case IOP_SHL: XShl_rr(ACC,TMP); break; case IOP_SHR: XShr_rr(ACC,TMP); break; case IOP_USHR: XUShr_rr(ACC,TMP); break; case IOP_AND: XAnd_rr(ACC,TMP); break; case IOP_OR: XOr_rr(ACC,TMP); break; case IOP_XOR: XXor_rr(ACC,TMP); break; default: ERROR; } best_int(); XJump_near(jend); PATCH_JUMP(jerr1); PATCH_JUMP(jerr2); begin_call(); XPush_c(GET_PC()); XPush_r(ACC); // acc and tmp are reversed in jit_number_op XPush_r(TMP); // XPush_r(VM); switch( op ) { case IOP_SHL: XCall_m(generic_shl); break; case IOP_SHR: XCall_m(generic_shr); break; case IOP_USHR: XCall_m(generic_ushr); break; case IOP_AND: XCall_m(generic_and); break; case IOP_OR: XCall_m(generic_or); break; case IOP_XOR: XCall_m(generic_xor); break; case IOP_LAST: // nothing break; } stack_pop(Esp,4); end_call(); PATCH_JUMP(jend); pop(1); END_BUFFER; } static void jit_array_access( jit_ctx *ctx, int n ) { INIT_BUFFER; int *jerr1, *jerr2; char *jend1, *jend2 = NULL, *jend3; int *jnot_array, *jbounds = NULL; is_int(ACC,true,jerr1); XMov_rp(TMP,ACC,0); XMov_rr(TMP2,TMP); XAnd_rc(TMP2,TAG_MASK); XCmp_rb(TMP2,VAL_ARRAY); XJump(JNeq,jnot_array); if( n > 0 ) { XUShr_rc(TMP,NEKO_TAG_BITS); XCmp_rc(TMP,n); XJump(JLte,jbounds); } XMov_rp(ACC,ACC,FIELD(n + 1)); XJump_near(jend1); if( n > 0 ) { PATCH_JUMP(jbounds); XMov_rc(ACC,CONST(val_null)); XJump_near(jend2); } PATCH_JUMP(jnot_array); XCmp_rb(TMP2,VAL_OBJECT); XJump(JNeq,jerr2); XPush_c(GET_PC()); XMov_rr(TMP,ACC); XMov_rc(ACC,CONST(alloc_int(n))); label(code->oo_get); stack_pop(Esp,1); XJump_near(jend3); PATCH_JUMP(jerr1); PATCH_JUMP(jerr2); runtime_error(4,false); PATCH_JUMP(jend1); PATCH_JUMP(jend2); PATCH_JUMP(jend3); END_BUFFER; } static void jit_make_env( jit_ctx *ctx, int esize ) { INIT_BUFFER; int *jerr1, *jerr2, *jok; int i; if( esize == -1 ) { XMov_rr(TMP2,TMP); // store esize } // check type t_function or t_jit is_int(ACC,true,jerr1); XMov_rp(TMP,ACC,FUNFIELD(t)); XCmp_rb(TMP,VAL_JITFUN); XJump(JEq,jok); XCmp_rb(TMP,VAL_FUNCTION); XJump(JNeq,jerr2); PATCH_JUMP(jok); // prepare args for alloc_module_function XPush_r(TMP); // acc->type stack_push(Esp,1); // empty cell stack_pad(2); XMov_rp(TMP,ACC,FUNFIELD(nargs)); XPush_r(TMP); XMov_rp(TMP,ACC,FUNFIELD(addr)); XPush_r(TMP); XMov_rp(TMP,ACC,FUNFIELD(module)); XPush_r(TMP); // call alloc_array(n) stack_pad(3); if( esize == -1 ) { XPush_r(TMP2); } else { XPush_c(esize); } XCall_m(alloc_array); if( esize == -1 ) { char *start; int *loop; XPop_r(TMP2); stack_pad(-3); // fill array start = buf.c; XMov_rp(TMP,SP,FIELD(0)); XMov_xr(ACC,TMP2,4,TMP); XMov_pc(SP,FIELD(0),0); stack_pop(SP,1); XSub_rc(TMP2,1); XCmp_rc(TMP2,0); XJump(JNeq,loop); *loop = (int)(start - buf.c); } else { stack_pop_pad(1,3); // fill array for(i=0;ioo_get); stack_pop(Esp,1); XJump_near(jend3); // check int32 index (with array) PATCH_JUMP(jerr2); XMov_rp(TMP2,ACC,FIELD(0)); XCmp_rb(TMP2,VAL_INT32); XJump(JNeq,jerr4); XMov_rc(ACC,CONST(val_null)); XJump_near(jend4); PATCH_JUMP(jerr1); PATCH_JUMP(jerr3); PATCH_JUMP(jerr4); runtime_error(4,false); // Invalid array access PATCH_JUMP(jend1); PATCH_JUMP(jend2); PATCH_JUMP(jend3); PATCH_JUMP(jend4); break; } case AccIndex: array_access(p); break; case AccIndex0: array_access(0); break; case AccIndex1: array_access(1); break; case AccField: { int *jerr1, *jerr2, *jend1, *loop, *no_resolver; char *jend2, *jend3, *start; is_int(ACC,true,jerr1); XMov_rp(TMP,ACC,FIELD(0)); XCmp_rb(TMP,VAL_OBJECT); XJump(JNeq,jerr2); XPush_r(ACC); XPush_r(VM); stack_pad(1); XMov_rr(VM,ACC); XPush_c(p); start = buf.c; XMov_rr(TMP,VM); XAdd_rc(TMP,FIELD(1)); XPush_r(TMP); XCall_m(otable_find); XCmp_rc(ACC,0); XJump(JNeq,jend1); stack_pop(Esp,1); XMov_rp(VM,VM,FIELD(3)); // acc = acc->proto XCmp_rc(VM,0); XJump(JNeq,loop); *loop = (int)(start - buf.c); stack_pop_pad(1,1); XPop_r(VM); XPop_r(ACC); XMov_rp(TMP,VM,VMFIELD(resolver)); XCmp_rc(TMP,0); XJump(JEq,no_resolver); XPush_c(CONST(alloc_int(p))); XPush_r(ACC); XPush_r(TMP); begin_call(); XCall_m(val_call2); end_call(); stack_pop(Esp,3); XJump_near(jend3); PATCH_JUMP(no_resolver); XMov_rc(ACC,CONST(val_null)); XJump_near(jend2); PATCH_JUMP(jerr1); PATCH_JUMP(jerr2); XPush_c(p); XPush_c(GET_PC()); label(code->invalid_access); PATCH_JUMP(jend1); stack_pop_pad(2,2); XPop_r(VM); stack_pop(Esp,1); XMov_rp(ACC,ACC,FIELD(0)); PATCH_JUMP(jend2); PATCH_JUMP(jend3); break; } case SetStack: XMov_pr(SP,FIELD(p),ACC); break; case SetGlobal: XMov_ar(CONST(p),ACC); break; case SetEnv: get_var_r(TMP,VEnv); XMov_rp(TMP2,TMP,FIELD(0)); XCmp_rc(TMP2,(p << NEKO_TAG_BITS) | VAL_ARRAY); XJump(JGt,jok); runtime_error(2,false); // Writing Outside Env PATCH_JUMP(jok); XMov_pr(TMP,FIELD(p+1),ACC); // val_array_ptr(env)[p] = acc break; case SetThis: set_var_r(VThis,ACC); break; case SetField: { int *jerr1, *jerr2; char *jend; XMov_rp(TMP,SP,FIELD(0)); is_int(TMP,true,jerr1); XMov_rp(TMP2,TMP,FIELD(0)); XCmp_rb(TMP2,VAL_OBJECT); XJump(JNeq,jerr2); // call otable_replace(table,field,acc) stack_pad(2); XPush_r(ACC); XPush_c(p); XAdd_rc(TMP,FIELD(1)); XPush_r(TMP); XCall_m(otable_replace); stack_pop(Esp,3); XMov_rp(ACC,Esp,FIELD(-1)); stack_pad(-2); XJump_near(jend); PATCH_JUMP(jerr1); PATCH_JUMP(jerr2); runtime_error(5,false); PATCH_JUMP(jend); pop(1); break; } case SetArray: { int *jerr1, *jerr2, *jerr3, *jnot_array, *jend1, *jend4; char *jend2, *jend3; XMov_rp(TMP,SP,FIELD(0)); // sp[0] : array/object is_int(TMP,true,jerr1); XMov_rp(TMP2,TMP,FIELD(0)); XAnd_rc(TMP2,TAG_MASK); XCmp_rb(TMP2,VAL_ARRAY); XJump(JNeq,jnot_array); XMov_rp(TMP2,SP,FIELD(1)); // sp[1] : index is_int(TMP2,false,jerr2); XMov_rp(TMP,TMP,FIELD(0)); // tmp = tmp->type XShr_rc(TMP2,1); XUShr_rc(TMP,NEKO_TAG_BITS); XCmp_rr(TMP2,TMP); XJump(JGte,jend1); XMov_rp(TMP,SP,FIELD(0)); XAdd_rc(TMP2,1); XMov_xr(TMP,TMP2,4,ACC); XJump_near(jend2); PATCH_JUMP(jnot_array); XCmp_rb(TMP2,VAL_OBJECT); XJump(JNeq,jerr3); XMov_rp(TMP2,SP,FIELD(1)); // index XPush_r(TMP2); XPush_c(GET_PC()); label(code->oo_set); stack_pop(Esp,2); XJump_near(jend3); // check int32 index (with array) PATCH_JUMP(jerr2); XMov_rp(TMP2,TMP2,FIELD(0)); XCmp_rb(TMP2,VAL_INT32); XJump(JEq,jend4); PATCH_JUMP(jerr1); PATCH_JUMP(jerr3); runtime_error(4,false); PATCH_JUMP(jend1); PATCH_JUMP(jend2); PATCH_JUMP(jend3); PATCH_JUMP(jend4); pop(2); break; } case SetIndex: { int *jerr1, *jerr2, *jnot_array, *jend1; char *jend2, *jend3; XMov_rp(TMP,SP,FIELD(0)); // sp[0] : array / object pop(1); is_int(TMP,true,jerr1); XMov_rp(TMP2,TMP,FIELD(0)); XAnd_rc(TMP2,TAG_MASK); XCmp_rb(TMP2,VAL_ARRAY); XJump(JNeq,jnot_array); XMov_rp(TMP2,TMP,FIELD(0)); XCmp_rc(TMP2,(p << NEKO_TAG_BITS) | VAL_ARRAY); // fake header XJump(JLte,jend1); XMov_pr(TMP,FIELD(p + 1),ACC); XJump_near(jend2); PATCH_JUMP(jnot_array); XCmp_rb(TMP2,VAL_OBJECT); XJump(JNeq,jerr2); XPush_c(CONST(alloc_int(p))); XPush_c(GET_PC()); label(code->oo_set); stack_pop(Esp,2); XJump_near(jend3); PATCH_JUMP(jerr1); PATCH_JUMP(jerr2); runtime_error(4,false); PATCH_JUMP(jend1); PATCH_JUMP(jend2); PATCH_JUMP(jend3); break; } case Push: stack_push(SP,1); XMov_pr(SP,FIELD(0),ACC); break; case Pop: if( p > 10 ) { pop_loop(p); } else { pop(p); } break; case Jump: jump(JAlways,p); break; case JumpIf: XCmp_rc(ACC,CONST(val_true)); jump(JEq,p); break; case JumpIfNot: XCmp_rc(ACC,CONST(val_true)); jump(JNeq,p); break; case Neq: { int *jnot1, *jnot2; char *jend; // call val_compare(sp[0],acc) XPush_r(ACC); XMov_rp(TMP,SP,FIELD(0)); XPush_r(TMP); begin_call(); XCall_m(val_compare); end_call(); stack_pop(Esp,2); pop(1); // test if ok and != invalid_comparison XCmp_rc(ACC,0); XJump(JNeq,jnot1); XCmp_rc(ACC,invalid_comparison); XJump(JEq,jnot2); XMov_rc(ACC,CONST(val_false)); XJump_near(jend); PATCH_JUMP(jnot1); PATCH_JUMP(jnot2); XMov_rc(ACC,CONST(val_true)); PATCH_JUMP(jend); break; } case Eq: test(JNeq); break; case Gt: test(JSignLte); break; case Gte: test(JSignLt); break; case Lt: test(JSignGte); break; case Lte: test(JSignGt); break; case Bool: case Not: { int *jfalse1, *jfalse2, *jfalse3; char *jend; XCmp_rc(ACC,CONST(val_false)); XJump(JEq,jfalse1); XCmp_rc(ACC,CONST(val_null)); XJump(JEq,jfalse2); XCmp_rc(ACC,CONST(alloc_int(0))); XJump(JEq,jfalse3); XMov_rc(ACC,CONST((op == Bool)?val_true:val_false)); XJump_near(jend); PATCH_JUMP(jfalse1); PATCH_JUMP(jfalse2); PATCH_JUMP(jfalse3); XMov_rc(ACC,CONST((op == Bool)?val_false:val_true)); PATCH_JUMP(jend); break; } case IsNull: { int *jnext; char *jend; XCmp_rc(ACC,CONST(val_null)); XJump(JNeq,jnext); XMov_rc(ACC,CONST(val_true)); XJump_near(jend); PATCH_JUMP(jnext); XMov_rc(ACC,CONST(val_false)); PATCH_JUMP(jend); break; } case IsNotNull: { int *jnext; char *jend; XCmp_rc(ACC,CONST(val_null)); XJump(JNeq,jnext); XMov_rc(ACC,CONST(val_false)); XJump_near(jend); PATCH_JUMP(jnext); XMov_rc(ACC,CONST(val_true)); PATCH_JUMP(jend); break; } case Call: call(NORMAL,p); break; case ObjCall: call(THIS_CALL,p); break; case TailCall: { int stack = (p >> 3); int nargs = (p & 7); int i = nargs; while( i > 0 ) { i--; XMov_rp(TMP,SP,FIELD(i)); XMov_pr(SP,FIELD(stack - nargs + i),TMP); } if( stack - nargs > 10 ) { pop_loop(stack - nargs); } else { pop(stack - nargs); } call(TAIL_CALL,nargs); // in case we return from a Primitive XRet(); } break; case Ret: if( p > 10 ) { pop_loop(p); } else { pop(p); } XRet(); break; case Add: number_op(OP_ADD); break; case Sub: number_op(OP_SUB); break; case Div: number_op(OP_DIV); break; case Mult: number_op(OP_MUL); break; case Mod: number_op(OP_MOD); break; case Shl: int_op(IOP_SHL); break; case Shr: int_op(IOP_SHR); break; case UShr: int_op(IOP_USHR); break; case And: int_op(IOP_AND); break; case Or: int_op(IOP_OR); break; case Xor: int_op(IOP_XOR); break; case New: XPush_r(ACC); XCall_m(alloc_object); stack_pop(Esp,1); break; case MakeArray: stack_pad(3); XPush_r(ACC); XPush_c(p + 1); XCall_m(alloc_array); XMov_rp(TMP,Esp,FIELD(1)); // tmp = saved acc XMov_pr(ACC,FIELD(1),TMP); // val_array_ptr(acc)[0] = tmp stack_pop_pad(2,3); if( p < 6 ) { i = 0; while( p > 0 ) { p--; i++; XMov_rp(TMP,SP,FIELD(p)); XMov_pr(ACC,FIELD(i + 1),TMP); XMov_pc(SP,FIELD(p),0); } stack_pop(SP,i); } else { char *start; int *loop; XMov_rc(TMP2,(p+1)); start = buf.c; XMov_rp(TMP,SP,FIELD(0)); XMov_pc(SP,FIELD(0),0); XMov_xr(ACC,TMP2,4,TMP); stack_pop(SP,1); XSub_rc(TMP2,1); XCmp_rc(TMP2,1); XJump(JNeq,loop); *loop = (int)(start - buf.c); } break; case MakeArray2: stack_pad(3); XPush_r(ACC); XPush_c(p + 1); XCall_m(alloc_array); XMov_rp(TMP,Esp,FIELD(1)); // tmp = saved acc XMov_pr(ACC,FIELD(p+1),TMP); // val_array_ptr(acc)[p] = tmp stack_pop_pad(2,3); if( p < 6 ) { i = 0; while( p > 0 ) { p--; i++; XMov_rp(TMP,SP,FIELD(p)); XMov_pr(ACC,FIELD(i),TMP); XMov_pc(SP,FIELD(p),0); } stack_pop(SP,i); } else { char *start; int *loop; XMov_rc(TMP2,p); start = buf.c; XMov_rp(TMP,SP,FIELD(0)); XMov_pc(SP,FIELD(0),0); XMov_xr(ACC,TMP2,4,TMP); stack_pop(SP,1); XSub_rc(TMP2,1); XCmp_rc(TMP2,0); XJump(JNeq,loop); *loop = (int)(start - buf.c); } break; case MakeEnv: XPush_c(GET_PC()); if( p >= MAX_ENV ) { XMov_rc(TMP,p); label(code->make_env_n); } else { label(code->make_env[p]); } stack_pop(Esp,1); break; case Last: XRet(); break; case Apply: { int *jerr1, *jerr2, *jnext, *jcall1, *jcall2, *jdone; char *jend, *start; is_int(ACC,true,jerr1); XMov_rp(TMP,ACC,FIELD(0)); XAnd_rc(TMP,TAG_MASK); XCmp_rb(TMP,VAL_FUNCTION); XJump(JNeq,jerr2); XMov_rp(TMP,ACC,FUNFIELD(nargs)); // what do we do depending of the number of args ? XCmp_rb(TMP,p); XJump(JSignGt,jnext); XJump(JEq,jcall1); XCmp_rb(TMP,VAR_ARGS); XJump(JEq,jcall2); PATCH_JUMP(jerr1); PATCH_JUMP(jerr2); runtime_error(8,false); // $apply // build the apply PATCH_JUMP(jnext); XPush_r(ACC); XPush_r(TMP); XAdd_rc(TMP,1); XPush_r(TMP); XCall_m(alloc_array); stack_pop(Esp,1); XPop_r(TMP); XPop_r(TMP2); // fill the env array XMov_pr(ACC,FIELD(1),TMP2); // env[0] = f i = p; while( i-- > 0 ) { // env[*pc-i] = sp[i] XMov_rp(TMP2,SP,FIELD(i)); XMov_pr(ACC,FIELD(1+p-i),TMP2); } pop(p); // fargs = fargs - *pc XSub_rc(TMP,p); // prepare stack for alloc_apply XPush_r(ACC); XPush_r(TMP); // fill the env array with null's XAdd_rc(ACC,(2+p) * 4 ); start = buf.c; XCmp_rb(TMP,0); XJump(JEq,jdone); XMov_pc(ACC,FIELD(0),CONST(val_null)); XAdd_rc(ACC,4); XSub_rc(TMP,1); // jump back to "start" { char *back; XJump_near(back); *back = (char)(start - (back + 1)); } // call alloc_apply PATCH_JUMP(jdone); XCall_m(neko_alloc_apply); stack_pop(Esp,2); XJump_near(jend); // direct call PATCH_JUMP(jcall1); PATCH_JUMP(jcall2); call(NORMAL,p); PATCH_JUMP(jend); break; } case Trap: { // save some vm->start on the stack XMov_rp(TMP,VM,VMFIELD(start)); XPush_r(TMP); XMov_rp(TMP,VM,VMFIELD(start)+FIELD(1)); XPush_r(TMP); XMov_rp(TMP,VM,VMFIELD(start)+FIELD(2)); XPush_r(TMP); XMov_rp(TMP,VM,VMFIELD(start)+FIELD(3)); XPush_r(TMP); // save magic, ebp , esp and ret_pc XMov_pc(VM,VMFIELD(start),CONST(jit_handle_trap)); XMov_pr(VM,VMFIELD(start)+FIELD(1),Ebp); XMov_pr(VM,VMFIELD(start)+FIELD(2),Esp); XMov_pc(VM,VMFIELD(start)+FIELD(3),-1); { jlist *t = (jlist*)alloc(sizeof(jlist)); ctx->buf = buf; t->pos = POS() - 4; t->target = (int)((int_val*)(int_val)p - ctx->module->code); t->next = ctx->traps; ctx->traps = t; } // neko_setup_trap(vm) XPush_r(ACC); stack_pad(3); XPush_r(VM); begin_call(); XCall_m(neko_setup_trap); end_call(); stack_pop(Esp,1); stack_pad(-3); XPop_r(ACC); break; } case EndTrap: { // check spmax - trap = sp get_var_r(TMP,VSpMax); get_var_r(TMP2,VTrap); XShl_rc(TMP2,2); XSub_rr(TMP,TMP2); XCmp_rr(TMP,SP); XJump(JEq,jok); runtime_error(9,false); // Invalid End Trap PATCH_JUMP(jok); // restore VM jmp_buf XPop_r(TMP); XMov_pr(VM,VMFIELD(start)+FIELD(3),TMP); XPop_r(TMP); XMov_pr(VM,VMFIELD(start)+FIELD(2),TMP); XPop_r(TMP); XMov_pr(VM,VMFIELD(start)+FIELD(1),TMP); XPop_r(TMP); XMov_pr(VM,VMFIELD(start),TMP); // trap = val_int(sp[5]) XMov_rp(TMP,SP,FIELD(5)); XShr_rc(TMP,1); set_var_r(VTrap,TMP); pop(6); break; } case TypeOf: { int *jnot_int; char *jend; is_int(ACC,false,jnot_int); XMov_rc(ACC,CONST(alloc_int(1))); // t_int != VAL_INT XJump_near(jend); PATCH_JUMP(jnot_int); XMov_rp(TMP,ACC,FIELD(0)); XAnd_rc(TMP,TAG_MASK); XMov_rc(TMP2,CONST(NEKO_TYPEOF)); XMov_rx(ACC,TMP2,TMP,4); PATCH_JUMP(jend); break; } case Compare: { int *jint; char *jend; // val_compare(sp[0],acc) XPush_r(ACC); XMov_rp(TMP,SP,FIELD(0)); XPush_r(TMP); begin_call(); XCall_m(val_compare); end_call(); stack_pop(Esp,2); XCmp_rc(ACC,invalid_comparison); XJump(JNeq,jint); XMov_rc(ACC,CONST(val_null)); XJump_near(jend); PATCH_JUMP(jint); XShl_rc(ACC,1); XOr_rc(ACC,1); PATCH_JUMP(jend); pop(1); break; } case PhysCompare: { int *jeq, *jlow; char *jend1, *jend2; XMov_rp(TMP,SP,FIELD(0)); pop(1); XCmp_rr(ACC,TMP); XJump(JEq,jeq); XJump(JSignLt,jlow); XMov_rc(ACC,CONST(alloc_int(-1))); XJump_near(jend1); PATCH_JUMP(jlow); XMov_rc(ACC,CONST(alloc_int(1))); XJump_near(jend2); PATCH_JUMP(jeq); XMov_rc(ACC,CONST(alloc_int(0))); PATCH_JUMP(jend1); PATCH_JUMP(jend2); break; } case Hash: { int *jerr1, *jerr2; char *jend; is_int(ACC,true,jerr1); XMov_rp(TMP,ACC,FIELD(0)); XAnd_rc(TMP,TAG_MASK); XCmp_rb(TMP,VAL_STRING); XJump(JNeq,jerr2); begin_call(); XAdd_rc(ACC,4); // val_string(acc) XPush_r(ACC); XCall_m(val_id); stack_pop(Esp,1); XShl_rc(ACC,1); XOr_rc(ACC,1); XJump_near(jend); PATCH_JUMP(jerr1); PATCH_JUMP(jerr2); runtime_error(10,false); // $hash PATCH_JUMP(jend); break; } case JumpTable: { int njumps = p / 2; int *jok1, *jok2; char *jnext1, *jnext2; is_int(ACC,true,jok1); XMov_rc(TMP2,njumps); XJump_near(jnext1); PATCH_JUMP(jok1); XCmp_rc(ACC,p); XJump(JLt,jok2); XMov_rc(TMP2,njumps); XJump_near(jnext2); PATCH_JUMP(jok2); XMov_rr(TMP2,ACC); XShr_rc(TMP2,1); PATCH_JUMP(jnext1); PATCH_JUMP(jnext2); get_var_r(TMP,VModule); // tmp = jit + tmp2 * 5 XMov_rp(TMP,TMP,FIELD(0)); // m->jit XAdd_rr(TMP,TMP2); XShl_rc(TMP2,2); XAdd_rr(TMP,TMP2); ctx->debug_wait = njumps; ctx->buf = buf; { int add_size = 6; int small_add_size = 3; int jump_rsize = 2; int delta = POS() + add_size + jump_rsize; if( IS_SBYTE(delta) ) delta += small_add_size - add_size; XAdd_rc(TMP,delta); XJump_r(TMP); } break; } case Loop: // nothing break; default: ERROR; } END_BUFFER; } #if defined(STACK_ALIGN_DEBUG) || defined(NEKO_JIT_DEBUG) # define MAX_OP_SIZE 1000 # define MAX_BUF_SIZE 1000 #else # define MAX_OP_SIZE 298 // Apply(4) + label(stack_expand) # define MAX_BUF_SIZE 500 #endif #define FILL_BUFFER(f,param,ptr) \ { \ jit_ctx *ctx; \ char *buf = alloc_private(MAX_BUF_SIZE); \ int size; \ ctx = jit_init_context(buf,MAX_BUF_SIZE); \ f(ctx,param); \ size = POS(); \ buf = alloc_jit_mem(size); \ code->ptr = buf; \ memcpy(buf,ctx->baseptr,size); \ ctx->buf.p = buf + size; \ ctx->baseptr = buf; \ jit_finalize_context(ctx); \ } #ifdef USE_MMAP static void free_jit_mem( void *_p ) { int *p = (int*)_p - 1; munmap(p,*p); } static void free_jit_abstract( value v ) { free_jit_mem(val_data(v)); } static char *alloc_jit_mem( int size ) { int *p; // add space for size size += sizeof(int); // round to next page size += (4096 - size%4096); p = (int*)mmap(NULL,size,PROT_READ|PROT_WRITE|PROT_EXEC,(MAP_PRIVATE|MAP_ANON),-1,0); if( p == (int*)-1 ) { buffer b = alloc_buffer("Failed to allocate JIT memory "); val_buffer(b,alloc_int(size>>10)); val_buffer(b,alloc_string("KB")); val_throw(buffer_to_string(b)); } *p = size; return (char*)(p + 1); } #else # define alloc_jit_mem alloc_private #endif void neko_init_jit() { int nstrings = sizeof(cstrings) / sizeof(const char *); int i; strings = alloc_root(nstrings); for(i=0;iboot; jit_handle_trap = code->handle_trap; } void neko_free_jit() { # ifdef USE_MMAP int i; for(i=0;inglobals && !val_is_function(m->globals[k]) ) k++; if( k == m->nglobals ) { *faddr = -1; return 0; } *faddr = (int_val*)((vfunction*)m->globals[k])->addr - m->code; return k; } void neko_module_jit( neko_module *m ) { unsigned int i = 0; int_val faddr; unsigned int fcursor = next_function(m,0,&faddr); jit_ctx *ctx = jit_init_context(NULL,0); ctx->pos = (int*)tmp_alloc(sizeof(int)*(m->codesize + 1)); ctx->module = m; while( i <= m->codesize ) { enum OPCODE op = m->code[i]; int curpos = POS(); ctx->pos[i] = curpos; ctx->curpc = i + 2; // resize buffer if( curpos + MAX_OP_SIZE > ctx->size ) { int nsize = ctx->size ? (ctx->size * 4) / 3 : ((m->codesize + 1) * 20); char *buf2; if( nsize - curpos < MAX_OP_SIZE ) nsize = curpos + MAX_OP_SIZE; buf2 = tmp_alloc(nsize); memcpy(buf2,ctx->baseptr,curpos); tmp_free(ctx->baseptr); ctx->baseptr = buf2; ctx->buf.p = buf2 + curpos; ctx->size = nsize; } // begin of function : check stack overflow if( faddr == i ) { INIT_BUFFER; label(code->stack_expand); END_BUFFER; fcursor = next_function(m,fcursor+1,&faddr); } // --------- debug --------- # ifdef NEKO_JIT_DEBUG if( ctx->debug_wait ) ctx->debug_wait--; else { INIT_BUFFER; XPush_r(ACC); // val_print(acc) XPush_r(ACC); XCall_m_real(val_print); stack_pop(Esp,1); // val_print(pc_pos) XPush_c(CONST(alloc_int(i))); XCall_m_real(val_print_2); stack_pop(Esp,1); // val_print(alloc_int(spmax - sp)) get_var_r(TMP,VSpMax); XSub_rr(TMP,SP); XShr_rc(TMP,1); XOr_rc(TMP,1); XPush_r(TMP); XCall_m_real(val_print_2); stack_pop(Esp,1); // val_print(alloc_int(csp - spmin)) XMov_rp(TMP2,VM,VMFIELD(spmin)); XMov_rr(TMP,CSP); XSub_rr(TMP,TMP2); XShr_rc(TMP,1); XOr_rc(TMP,1); XPush_r(TMP); XCall_m_real(val_print_3); stack_pop(Esp,1); XPop_r(ACC); END_BUFFER; } # endif i++; jit_opcode(ctx,op,(int)m->code[i]); # ifdef _DEBUG { int bytes = POS() - curpos; if( bytes > MAX_OP_SIZE ) ERROR; } # endif i += parameter_table[op]; } // FINALIZE { int csize = POS(); char *rbuf = alloc_jit_mem(csize); memcpy(rbuf,ctx->baseptr,csize); tmp_free(ctx->baseptr); ctx->baseptr = rbuf; ctx->buf.p = rbuf + csize; ctx->size = csize; # ifdef USE_MMAP m->jit_gc = alloc_abstract(NULL,rbuf); val_gc(m->jit_gc,free_jit_abstract); # endif # ifdef NEKO_JIT_DEBUG printf("Jit size = %d ( x%.1f )\n",csize,csize * 1.0 / ((m->codesize + 1) * 4)); # endif jit_finalize_context(ctx); } // UPDATE GLOBALS { for(i=0;inglobals;i++) { vfunction *f = (vfunction*)m->globals[i]; if( !val_is_int(f) && val_tag(f) == VAL_FUNCTION && f->module == m ) { int pc = (int)((int_val*)f->addr - m->code); f->t = VAL_JITFUN; f->addr = (char*)ctx->baseptr + ctx->pos[pc]; } } } m->jit = ctx->baseptr; tmp_free(ctx->pos); } #else // NEKO_JIT_ENABLE char *jit_boot_seq = NULL; char *jit_handle_trap = (char*)&jit_boot_seq; void neko_init_jit() { } void neko_free_jit() { } int neko_can_jit() { return 0; } void neko_module_jit( neko_module *m ) { } #endif /* ************************************************************************ */ neko-2-4-0/vm/load.c000066400000000000000000000265261464615675700142140ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include "vm.h" #include "neko_mod.h" #ifndef NEKO_WINDOWS # include #endif extern field id_cache; extern field id_path; extern field id_loader_libs; DEFINE_KIND(k_loader_libs); #ifdef NEKO_PROF typedef void (*callb)( const char *name, neko_module *m, int *tot ); typedef struct { callb callb; int tot; } dump_param; static void profile_total( const char *name, neko_module *m, int *tot ) { unsigned int i; unsigned int n = 0; for(i=0;icodesize;i++) n += (int)m->code[PROF_SIZE+i]; *tot += n; } static void profile_summary( const char *name, neko_module *m, int *ptr ) { unsigned int i; unsigned int tot = 0; for(i=0;icodesize;i++) tot += (int)m->code[PROF_SIZE+i]; printf("%10d %-4.1f%% %s\n",tot,(tot * 100.0f) / (*ptr),name); } static void profile_details( const char *name, neko_module *m, int *tot ) { unsigned int i; value *dbg = val_is_null(m->debuginf)?NULL:val_array_ptr(m->debuginf); printf("Details for : %s[%d]\n",name,m->codesize); for(i=0;icodesize;i++) { int c = (int)m->code[PROF_SIZE+i]; if( c > 0 ) { if( dbg ) val_print(dbg[i]); printf(" %-4X %d\n",i,c); } } printf("\n"); } static void profile_functions( const char *name, neko_module *m, int *tot ) { unsigned int i; value *dbg = val_is_null(m->debuginf)?NULL:val_array_ptr(m->debuginf); for(i=0;inglobals;i++) { value v = m->globals[i]; if( val_is_function(v) && val_type(v) == VAL_FUNCTION && ((vfunction*)v)->module == m ) { int pos = (int)(((int_val)((vfunction*)v)->addr - (int_val)m->code) / sizeof(int_val)); if( m->code[PROF_SIZE+pos] > 0 ) { printf("%-8d %-4d %-20s %X ",m->code[PROF_SIZE+pos],i,name,pos); if( dbg ) val_print(dbg[pos]); printf("\n"); } } } } static void dump_module( value v, field f, void *p ) { value vname; const char *name; if( !val_is_kind(v,neko_kind_module) ) return; vname = val_field_name(f); name = val_is_null(vname)?"???":val_string(vname); ((dump_param*)p)->callb( name, (neko_module*)val_data(v), &((dump_param*)p)->tot ); } static value dump_prof() { dump_param p; value o = val_this(); value cache; val_check(o,object); cache = val_field(o,id_cache); val_check(cache,object); p.tot = 0; p.callb = profile_total; val_iter_fields(cache,dump_module,&p); printf("Summary :\n"); p.callb = profile_summary; val_iter_fields(cache,dump_module,&p); printf("%10d\n\n",p.tot); printf("Functions :\n"); p.callb = profile_functions; val_iter_fields(cache,dump_module,&p); printf("\n"); p.callb = profile_details; val_iter_fields(cache,dump_module,&p); return val_true; } #endif #ifdef NEKO_WINDOWS # undef ERROR # include # define dlopen(l,p) (void*)LoadLibrary(l) # define dlsym(h,n) GetProcAddress((HANDLE)h,n) #endif EXTERN value neko_select_file( value path, const char *file, const char *ext ) { struct stat s; value ff; buffer b = alloc_buffer(file); buffer_append(b,ext); ff = buffer_to_string(b); if( stat(val_string(ff),&s) == 0 ) { char *p = strchr(file,'/'); if( p == NULL ) p = strchr(file,'\\'); if( p != NULL ) return ff; b = alloc_buffer("./"); buffer_append(b,file); buffer_append(b,ext); return buffer_to_string(b); } while( val_is_array(path) && val_array_size(path) == 2 ) { value p = val_array_ptr(path)[0]; buffer b = alloc_buffer(NULL); path = val_array_ptr(path)[1]; val_buffer(b,p); val_buffer(b,ff); p = buffer_to_string(b); if( stat(val_string(p),&s) == 0 ) return p; } return ff; } static void open_module( value path, const char *mname, reader *r, readp *p ) { FILE *f; value fname; char *ext = strrchr(mname,'.'); if( ext && ext[1] == 'n' && ext[2] == 0 ) fname = neko_select_file(path,mname,""); else fname = neko_select_file(path,mname,".n"); f = fopen(val_string(fname),"rb"); if( f == NULL ) { buffer b = alloc_buffer("Module not found : "); buffer_append(b,mname); bfailure(b); } *r = neko_file_reader; *p = f; } static void close_module( readp p ) { fclose(p); } typedef struct _liblist { char *name; void *handle; struct _liblist *next; } liblist; typedef value (*PRIM0)(); static void *load_primitive( const char *prim, int nargs, value path, liblist **libs ) { char *pos = strchr(prim,'@'); int len; liblist *l; PRIM0 ptr; if( pos == NULL ) return NULL; l = *libs; *pos = 0; len = (int)strlen(prim) + 1; # ifndef NEKO_STANDALONE while( l != NULL ) { if( memcmp(l->name,prim,len) == 0 ) break; l = l->next; } # endif if( l == NULL ) { void *h; value pname = pname = neko_select_file(path,prim,".ndll"); #ifdef NEKO_STANDALONE # ifdef NEKO_WINDOWS h = (void*)GetModuleHandle(NULL); # else h = dlopen(NULL,RTLD_LAZY); # endif #else h = dlopen(val_string(pname),RTLD_LAZY); #endif if( h == NULL ) { buffer b = alloc_buffer("Failed to load library : "); val_buffer(b,pname); #ifndef NEKO_WINDOWS buffer_append(b," ("); buffer_append(b,dlerror()); buffer_append(b,")"); #endif *pos = '@'; bfailure(b); } l = (liblist*)alloc(sizeof(liblist)); l->handle = h; l->name = alloc_private(len); memcpy(l->name,prim,len); l->next = *libs; *libs = l; ptr = (PRIM0)dlsym(l->handle,"__neko_entry_point"); if( ptr != NULL ) ((PRIM0)ptr())(); } *pos++ = '@'; { char buf[100]; if( strlen(pos) > 90 ) return NULL; if( nargs == VAR_ARGS ) sprintf(buf,"%s__MULT",pos); else sprintf(buf,"%s__%d",pos,nargs); ptr = (PRIM0)dlsym(l->handle,buf); if( ptr == NULL ) return NULL; return ptr(); } } static value init_path( const char *path ) { value l = val_null, tmp; char *p, *p2; char *allocated = NULL; #ifdef NEKO_WINDOWS char exe_path[MAX_PATH]; if( path == NULL ) { # ifdef NEKO_STANDALONE # define SELF_DLL NULL # else # define SELF_DLL "neko.dll" # endif if( GetModuleFileName(GetModuleHandle(SELF_DLL),exe_path,MAX_PATH) == 0 ) return val_null; p = strrchr(exe_path,'\\'); if( p == NULL ) return val_null; *p = 0; path = exe_path; } #else if( path == NULL ) { allocated = strdup(NEKO_MODULE_PATH ":/usr/local/lib/neko:/usr/lib/neko:/usr/local/bin:/usr/bin"); path = allocated; } #endif while( true ) { // windows drive letter (same behavior expected on all os) if( *path && path[1] == ':' ) { p = strchr(path+2,':'); p2 = strchr(path+2,';'); } else { p = strchr(path,':'); p2 = strchr(path,';'); } if( p == NULL || (p2 != NULL && p2 < p) ) p = p2; if( p != NULL ) *p = 0; tmp = alloc_array(2); if( (p && p[-1] != '/' && p[-1] != '\\') || (!p && path[strlen(path)-1] != '/' && path[strlen(path)-1] != '\\') ) { buffer b = alloc_buffer(path); char c = '/'; buffer_append_sub(b,&c,1); val_array_ptr(tmp)[0] = buffer_to_string(b); } else val_array_ptr(tmp)[0] = alloc_string(path); val_array_ptr(tmp)[1] = l; l = tmp; if( p != NULL ) *p = (p == p2)?';':':'; else break; path = p+1; } if( allocated != NULL ) free(allocated); return l; } typedef value (*stats_callback)( value, value, value, value, value, value ); static value stats_proxy( value p1, value p2, value p3, value p4, value p5, value p6 ) { neko_vm *vm = NEKO_VM(); value env = vm->env; value ret; if( vm->pstats ) vm->pstats(vm,val_string(val_array_ptr(env)[0]),1); ret = ((stats_callback)((int_val)val_array_ptr(vm->env)[1]&~1))(p1,p2,p3,p4,p5,p6); if( vm->pstats ) vm->pstats(vm,val_string(val_array_ptr(env)[0]),0); return ret; } static value loader_loadprim( value prim, value nargs ) { value o = val_this(); value libs; val_check(o,object); val_check(prim,string); val_check(nargs,int); libs = val_field(o,id_loader_libs); val_check_kind(libs,k_loader_libs); if( val_int(nargs) >= 10 || val_int(nargs) < -1 ) neko_error(); { neko_vm *vm = NEKO_VM(); void *ptr = load_primitive(val_string(prim),val_int(nargs),val_field(o,id_path),(liblist**)(void*)&val_data(libs)); vfunction *f; if( ptr == NULL ) { buffer b = alloc_buffer("Primitive not found : "); val_buffer(b,prim); buffer_append(b,"("); val_buffer(b,nargs); buffer_append(b,")"); bfailure(b); } f = (vfunction*)alloc_function(ptr,val_int(nargs),val_string(copy_string(val_string(prim),val_strlen(prim)))); if( vm->pstats && val_int(nargs) <= 6 ) { value env = alloc_array(2); val_array_ptr(env)[0] = f->module; val_array_ptr(env)[1] = (value)(((int_val)f->addr) | 1); f->addr = stats_proxy; f->env = env; } return (value)f; } } static value loader_loadmodule( value mname, value vthis ) { value o = val_this(); value cache; val_check(o,object); val_check(mname,string); val_check(vthis,object); cache = val_field(o,id_cache); val_check(cache,object); { reader r; readp p; neko_module *m; neko_vm *vm = NEKO_VM(); field mid = val_id(val_string(mname)); value mv = val_field(cache,mid); if( val_is_kind(mv,neko_kind_module) ) { m = (neko_module*)val_data(mv); return m->exports; } open_module(val_field(o,id_path),val_string(mname),&r,&p); if( vm->fstats ) vm->fstats(vm,"neko_read_module",1); m = neko_read_module(r,p,vthis); if( vm->fstats ) vm->fstats(vm,"neko_read_module",0); close_module(p); if( m == NULL ) { buffer b = alloc_buffer("Invalid module : "); val_buffer(b,mname); bfailure(b); } m->name = alloc_string(val_string(mname)); mv = alloc_abstract(neko_kind_module,m); alloc_field(cache,mid,mv); if( vm->fstats ) vm->fstats(vm,val_string(mname),1); neko_vm_execute(neko_vm_current(),m); if( vm->fstats ) vm->fstats(vm,val_string(mname),0); return m->exports; } } EXTERN value neko_default_loader( char **argv, int argc ) { value o = alloc_object(NULL); value args = alloc_array(argc); int i; for(i=0;i #include #include #include "neko_vm.h" #include "neko_elf.h" #ifdef NEKO_WINDOWS # include #else # include # include #endif #ifdef NEKO_MAC # include # include #endif #ifdef NEKO_BSD # include # include #endif #ifdef NEKO_POSIX # include #endif #ifdef __GNUC__ #ifdef ABI_ELF # define SEPARATE_SECTION_FOR_BYTECODE #endif #endif #ifdef SEPARATE_SECTION_FOR_BYTECODE // Make a special section header that can be repurposed to encapsulate // any attached bytecode so that it will not be stripped away by // accident... const unsigned int BYTECODE_SEC __attribute__((__section__(".nekobytecode"))) = 0x00; #endif #ifdef NEKO_STANDALONE extern void neko_standalone_init(); extern void neko_standalone_error( const char *str ); extern value neko_standalone_loader( char **arv, int argc ); # define default_loader neko_standalone_loader #else # define default_loader neko_default_loader #endif static FILE *self; extern void neko_stats_measure( neko_vm *vm, const char *kind, int start ); extern value neko_stats_build( neko_vm *vm ); static char *executable_path() { #if defined(NEKO_WINDOWS) static char path[MAX_PATH]; if( GetModuleFileName(NULL,path,MAX_PATH) == 0 ) return NULL; return path; #elif defined(NEKO_MAC) static char path[MAXPATHLEN+1]; uint32_t path_len = MAXPATHLEN; if ( _NSGetExecutablePath(path, &path_len) ) return NULL; return path; #elif defined(NEKO_BSD) int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; static char path[MAXPATHLEN]; size_t cb = sizeof(path); sysctl(mib, 4, path, &cb, NULL, 0); if (!cb) return NULL; return path; #elif defined(NEKO_LINUX) static char path[PATH_MAX]; int length = readlink("/proc/self/exe", path, sizeof(path)); if( length < 0 || length >= PATH_MAX ) { char *p = getenv(" "); // for upx if( p == NULL ) p = getenv("_"); return p; } path[length] = '\0'; return path; #else return getenv("_"); #endif } int neko_has_embedded_module( neko_vm *vm ) { char *exe = executable_path(); unsigned char id[8]; int beg=-1, end=0; if( exe == NULL ) return 0; #ifdef SEPARATE_SECTION_FOR_BYTECODE /* Look for a .nekobytecode section in the executable..., there is always a small section */ if ( val_true != elf_find_embedded_bytecode(exe,&beg,&end) || end-beg <= 8) { /* Couldn't find a big enough .nekobytecode section, fallback to looking at the end of the executable... */ beg = -1; end = 0; } #endif /* Back up eight bytes to the possible bytecode signature... */ end -= 8; self = fopen(exe,"rb"); if( self == NULL ) return 0; fseek(self,end,(end<0)?SEEK_END:SEEK_SET); if( fread(id,1,8,self) != 8 || id[0] != 'N' || id[1] != 'E' || id[2] != 'K' || id[3] != 'O' ) { fclose(self); return 0; } if ( -1 == beg ) { beg = id[4] | id[5] << 8 | id[6] << 16; } fseek(self,beg,(beg<0)?SEEK_END:SEEK_SET); // flags if( (id[7] & 1) == 0 ) neko_vm_jit(vm,1); return 1; } static void report( neko_vm *vm, value exc, int isexc ) { int i; buffer b = alloc_buffer(NULL); value st = neko_exc_stack(vm); for(i=0;i #else # define _CrtSetDbgFlag(x) #endif #ifdef NEKO_POSIX static void handle_signal( int signal ) { if( signal == SIGPIPE ) val_throw(alloc_string("Broken pipe")); else val_throw(alloc_string("Segmentation fault")); } #endif int main( int argc, char *argv[] ) { neko_vm *vm; value mload; int r; _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); neko_global_init(); vm = neko_vm_alloc(NULL); neko_vm_select(vm); # ifdef NEKO_STANDALONE neko_standalone_init(); # endif # ifdef NEKO_POSIX struct sigaction act; act.sa_sigaction = NULL; act.sa_handler = handle_signal; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGPIPE,&act,NULL); # endif if( !neko_has_embedded_module(vm) ) { int jit = 1; int stats = 0; while( argc > 1 ) { if( strcmp(argv[1],"-interp") == 0 ) { argc--; argv++; jit = 0; continue; } if( strcmp(argv[1],"-stats") == 0 ) { argc--; argv++; stats = 1; neko_vm_set_stats(vm,neko_stats_measure,neko_stats_measure); neko_stats_measure(vm,"total",1); continue; } if( strcmp(argv[1],"-version") == 0 ) { argc--; argv++; printf("%d.%d.%d\n",NEKO_VERSION_MAJOR,NEKO_VERSION_MINOR,NEKO_VERSION_PATCH); return 0; } break; } # ifdef NEKO_POSIX if( jit ) sigaction(SIGSEGV,&act,NULL); # endif neko_vm_jit(vm,jit); if( argc == 1 ) { # ifdef NEKO_STANDALONE report(vm,alloc_string("No embedded module in this executable"),0); # else printf("NekoVM %d.%d.%d (c)2005-%d Haxe Foundation\n Usage : neko \n",NEKO_VERSION_MAJOR,NEKO_VERSION_MINOR,NEKO_VERSION_PATCH,NEKO_BUILD_YEAR); # endif mload = NULL; r = 1; } else { mload = default_loader(argv+2,argc-2); r = execute_file(vm,argv[1],mload); } if( stats ) { value v; neko_stats_measure(vm,"total",0); v = neko_stats_build(vm); val_print(alloc_string("TOT\tTIME\tCOUNT\tNAME\n")); while( v != val_null ) { char buf[256]; value *s = val_array_ptr(v); int errors = val_int(s[4]); sprintf(buf,"%d\t%d\t%d\t%s%c", val_int(s[1]), val_int(s[2]), val_int(s[3]), val_string(s[0]), errors?' ':'\n'); if( errors ) sprintf(buf+strlen(buf),"ERRORS=%d\n",errors); val_print(alloc_string(buf)); v = s[5]; } } } else { mload = default_loader(argv+1,argc-1); r = neko_execute_self(vm,mload); } if( mload != NULL && val_field(mload,val_id("dump_prof")) != val_null ) val_ocall0(mload,val_id("dump_prof")); vm = NULL; mload = NULL; neko_vm_select(NULL); neko_global_free(); return r; } /* ************************************************************************ */ neko-2-4-0/vm/module.c000066400000000000000000000347511464615675700145610ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "neko_mod.h" #include "vm.h" #define PARAMETER_TABLE #define STACK_TABLE #include "opcodes.h" DEFINE_KIND(neko_kind_module); /* Endianness macros. */ #ifdef NEKO_BSD # include #endif /* *_TO_LE(X) converts (X) to little endian. */ #ifdef NEKO_LITTLE_ENDIAN # define LONG_TO_LE(X) (X) # define SHORT_TO_LE(X) (X) #else # define LONG_TO_LE(X) ((((X) >> 24) & 0xff) | \ (((X) >> 8) & 0xff00) | (((X) & 0xff00) << 8) | \ (((X) & 0xff) << 24)) # define SHORT_TO_LE(X) ((((X) >> 8) & 0xff) | (((X) & 0xff) << 8)) #endif #define MAXSIZE 0x100 #define ERROR() { free(tmp); return NULL; } #define READ(buf,len) if( r(p,buf,len) == -1 ) ERROR() #ifdef NEKO_64BITS static void read_long( reader r, readp p, unsigned int *i ) { unsigned char c[4]; int n; r(p,c,4); n = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); *i = LONG_TO_LE(n); } static void read_short( reader r, readp p, unsigned short *i ) { unsigned char c[2]; int n; r(p,c,2); n = c[0] | (c[1] << 8); *i = SHORT_TO_LE(n); } # define READ_LONG(var) read_long(r,p,&(var)) # define READ_SHORT(var) read_short(r,p,&(var)) #else # define READ_LONG(var) READ(&(var), 4); var = LONG_TO_LE(var) # define READ_SHORT(var) READ(&(var), 2); var = SHORT_TO_LE(var) #endif extern field id_loader; extern field id_exports; extern value *neko_builtins; extern value neko_alloc_module_function( void *m, int_val pos, int nargs ); extern void neko_module_jit( neko_module *m ); EXTERN int neko_is_big_endian() { #ifdef NEKO_LITTLE_ENDIAN return 0; #else return 1; #endif } static int read_string( reader r, readp p, char *buf ) { int i = 0; char c; while( i < MAXSIZE ) { if( r(p,&c,1) == -1 ) return -1; buf[i++] = c; if( c == 0 ) return i; } return -1; } static value get_builtin( neko_module *m, field id ) { value f = val_field(*neko_builtins,id); if( val_is_null(f) ) { unsigned int i; for(i=0;infields;i++) if( val_id(val_string(m->fields[i])) == id ) { buffer b = alloc_buffer("Builtin not found : "); val_buffer(b,m->fields[i]); bfailure(b); } failure("Builtin not found"); } return f; } #define UNKNOWN ((unsigned char)-1) static int neko_check_stack( neko_module *m, unsigned char *tmp, unsigned int i, int stack, int istack ) { unsigned int itmp; while( true ) { int c = (int)m->code[i]; int s = stack_table[c]; if( tmp[i] == UNKNOWN ) tmp[i] = stack; else if( tmp[i] != stack ) return 0; else return 1; if( s == P ) stack += (int)m->code[i+1]; else if( s == -P ) stack -= (int)m->code[i+1]; else stack += s; // 4 because it's the size of a push-infos needed in case of subcall if( stack < istack || stack >= MAX_STACK_PER_FUNCTION - 4 ) return 0; switch( c ) { case Jump: case JumpIf: case JumpIfNot: case Trap: itmp = (int)(((int_val*)m->code[i+1]) - m->code); if( tmp[itmp] == UNKNOWN ) { if( c == Trap ) stack -= s; if( !neko_check_stack(m,tmp,itmp,stack,istack) ) return 0; if( c == Trap ) stack += s; } else if( tmp[itmp] != stack ) return 0; if( c == Jump ) return 1; break; case JumpTable: itmp = (int)m->code[i+1]; i += itmp; while( itmp > 0 ) { itmp -= 2; if( m->code[i - itmp] != Jump ) return 0; if( !neko_check_stack(m,tmp,i - itmp,stack,istack) ) return 0; } break; case AccStack: case SetStack: if( m->code[i+1] >= stack ) return 0; break; case AccStack0: if( 0 >= stack ) return 0; break; case AccStack1: if( 1 >= stack ) return 0; break; case Last: if( stack != 0 ) return 0; return 1; case Ret: if( m->code[i+1] != stack ) return 0; return 1; case ObjCall: stack--; if( stack < istack ) return 0; break; case TailCall: if( stack - (m->code[i+1] & 7) < istack || (m->code[i+1]>>3) != stack ) return 0; return 1; } i += parameter_table[c]?2:1; } return 1; } static void append_array( value *arr, int pos, value v ) { int len = val_array_size(*arr); if( pos >= len ) { value a2 = alloc_array((len * 3) / 2); memcpy(val_array_ptr(a2),val_array_ptr(*arr),len * sizeof(value)); *arr = a2; } val_array_ptr(*arr)[pos] = v; } #define SETBIT(b) { \ if( (i & 31) == 0 ) { \ bits++; \ bits->base = pos_index; \ bits->bits = 0; \ } else \ bits->bits |= b << (31 - (i & 31)); \ i++; \ } static void *read_debug_infos( reader r, readp p, char *tmp, neko_module *m ) { unsigned int i; int curline = 0; value curfile; unsigned int npos; unsigned int nfiles; unsigned char c,c2; value files; value positions, pp; neko_debug *bits; int pos_index = -1; int lot_of_files = 0; READ(&c,1); if( c >= 0x80 ) { READ(&c2,1); nfiles = ((c & 0x7F) << 8) | c2; lot_of_files = 1; } else nfiles = c; if( nfiles == 0 ) ERROR(); files = alloc_array(nfiles); for(i=0;icodesize ) ERROR(); curfile = val_array_ptr(files)[0]; positions = alloc_array(2 + (npos / 20)); bits = (neko_debug *)alloc_private(sizeof(neko_debug) * ((npos + 31) >> 5)); m->dbgidxs = bits; bits--; i = 0; pp = NULL; while( i < npos ) { READ(&c,1); if( c & 1 ) { c >>= 1; if( lot_of_files ) { READ(&c2,1); nfiles = (c << 8) | c2; } else nfiles = c; if( nfiles >= (unsigned int)val_array_size(files) ) ERROR(); curfile = val_array_ptr(files)[nfiles]; pp = NULL; } else if( c & 2 ) { int delta = c >> 6; int count = (c >> 2) & 15; if( i + count > npos ) ERROR(); if( pp == NULL ) { pp = alloc_array(2); val_array_ptr(pp)[0] = curfile; val_array_ptr(pp)[1] = alloc_int(curline); append_array(&positions,++pos_index,pp); SETBIT(1); count--; } while( count-- ) SETBIT(0); if( delta ) { curline += delta; pp = NULL; } } else if( c & 4 ) { curline += c >> 3; pp = alloc_array(2); val_array_ptr(pp)[0] = curfile; val_array_ptr(pp)[1] = alloc_int(curline); append_array(&positions,++pos_index,pp); SETBIT(1); } else { unsigned char b2; unsigned char b3; READ(&b2,1); READ(&b3,1); curline = (c >> 3) | (b2 << 5) | (b3 << 13); pp = alloc_array(2); val_array_ptr(pp)[0] = curfile; val_array_ptr(pp)[1] = alloc_int(curline); append_array(&positions,++pos_index,pp); SETBIT(1); } } // table copy pos_index++; m->dbgtbl = alloc_array(pos_index); memcpy(val_array_ptr(m->dbgtbl),val_array_ptr(positions),pos_index * sizeof(value)); return m; } neko_module *neko_read_module( reader r, readp p, value loader ) { register unsigned int i; unsigned int itmp; unsigned char t; unsigned short stmp; register char *tmp = NULL; unsigned char version = 1; register neko_module *m = (neko_module*)alloc(sizeof(neko_module)); neko_vm *vm = NEKO_VM(); READ_LONG(itmp); if( itmp != 0x4F4B454E ) ERROR(); READ_LONG(m->nglobals); READ_LONG(m->nfields); READ_LONG(m->codesize); if( (int)m->nglobals < 0 || m->nglobals > 0xFFFF || (int)m->nfields < 0 || m->nfields > 0xFFFF || (int)m->codesize < 0 || m->codesize > 0xFFFFFF ) ERROR(); tmp = (char*)malloc(sizeof(char)*(((m->codesize+1)>MAXSIZE)?(m->codesize+1):MAXSIZE)); m->jit = NULL; m->jit_gc = NULL; m->dbgtbl = val_null; m->dbgidxs = NULL; m->globals = (value*)alloc(m->nglobals * sizeof(value)); m->fields = (value*)alloc(sizeof(value*)*m->nfields); m->loader = loader; m->exports = alloc_object(NULL); if( vm->fstats ) vm->fstats(vm,"neko_read_module_data",1); alloc_field(m->exports,neko_id_module,alloc_abstract(neko_kind_module,m)); // Init global table for(i=0;inglobals;i++) { READ(&t,1); switch( t ) { case 1: if( read_string(r,p,tmp) == -1 ) ERROR(); m->globals[i] = val_null; break; case 2: READ_LONG(itmp); if( (itmp & 0xFFFFFF) >= m->codesize ) ERROR(); m->globals[i] = neko_alloc_module_function(m,(itmp&0xFFFFFF),(itmp >> 24)); break; case 3: READ_SHORT(stmp); m->globals[i] = alloc_empty_string(stmp); READ(val_string(m->globals[i]),stmp); break; case 4: if( read_string(r,p,tmp) == -1 ) ERROR(); m->globals[i] = alloc_float( atof(tmp) ); break; case 5: if( !read_debug_infos(r,p,tmp,m) ) { tmp = NULL; // already free in read_debug_infos ERROR(); } m->globals[i] = val_null; break; case 6: READ(&version,1); m->globals[i] = val_null; break; default: ERROR(); break; } } for(i=0;infields;i++) { if( read_string(r,p,tmp) == -1 ) ERROR(); m->fields[i] = alloc_string(tmp); } if( vm->fstats ) { vm->fstats(vm,"neko_read_module_data",0); vm->fstats(vm,"neko_read_module_code",1); } #ifdef NEKO_PROF if( m->codesize >= PROF_SIZE ) ERROR(); m->code = (int_val*)alloc_private(sizeof(int_val)*(m->codesize+PROF_SIZE)); memset(m->code+PROF_SIZE,0,m->codesize*sizeof(int_val)); #else m->code = (int_val*)alloc_private(sizeof(int_val)*(m->codesize+1)); #endif i = 0; // Unpack opcodes while( i < m->codesize ) { READ(&t,1); tmp[i] = 1; switch( t & 3 ) { case 0: m->code[i++] = (t >> 2); break; case 1: m->code[i++] = (t >> 3); tmp[i] = 0; m->code[i++] = (t >> 2) & 1; break; case 2: m->code[i++] = (t >> 2); if( t == 2 ) { // extra opcodes READ(&t,1); m->code[i-1] = t; } else { READ(&t,1); tmp[i] = 0; m->code[i++] = t; } break; case 3: m->code[i++] = (t >> 2); READ_LONG(itmp); tmp[i] = 0; m->code[i++] = (int)itmp; break; } } tmp[i] = 1; m->code[i] = Last; if( vm->fstats ) { vm->fstats(vm,"neko_read_module_code",0); vm->fstats(vm,"neko_read_module_check",1); } // Check bytecode for(i=0;icodesize;i++) { register int c = (int)m->code[i]; itmp = (unsigned int)m->code[i+1]; if( c >= Last || tmp[i+1] == parameter_table[c] ) ERROR(); // Additional checks and optimizations switch( m->code[i] ) { case AccGlobal: case SetGlobal: if( itmp >= m->nglobals ) ERROR(); m->code[i+1] = (int_val)(m->globals + itmp); break; case Jump: case JumpIf: case JumpIfNot: case Trap: itmp += i; if( itmp > m->codesize || !tmp[itmp] ) ERROR(); m->code[i+1] = (int_val)(m->code + itmp); break; case AccInt: if( need_32_bits((int)itmp) ) m->code[i] = AccInt32; else m->code[i+1] = (int_val)alloc_int((int)itmp); break; case AccIndex: m->code[i+1] += 2; break; case AccStack: m->code[i+1] += 2; itmp = (unsigned int)m->code[i+1]; case SetStack: if( ((int)itmp) < 0 ) ERROR(); break; case Ret: case Pop: case AccEnv: case SetEnv: if( ((int)itmp) < 0 ) ERROR(); break; case AccBuiltin: { field f = (field)(int_val)itmp; if( f == id_loader ) m->code[i+1] = (int_val)loader; else if( f == id_exports ) m->code[i+1] = (int_val)m->exports; else m->code[i+1] = (int_val)get_builtin(m,f); } break; case Call: case ObjCall: if( itmp > CALL_MAX_ARGS ) ERROR(); break; case TailCall: if( (itmp&7) > CALL_MAX_ARGS ) ERROR(); break; case Apply: if( itmp == 0 || itmp >= CALL_MAX_ARGS ) ERROR(); break; case MakeEnv: if( itmp > 0xFF ) failure("Too much big environment"); break; case MakeArray: if( itmp > 0x10000 ) failure("Too much big array"); if( version >= 2 ) m->code[i] = MakeArray2; break; case JumpTable: if( itmp > 512 || i + 1 + itmp * 2 >= m->codesize ) ERROR(); m->code[i+1] <<= 1; break; } if( !tmp[i+1] ) i++; } // Check stack preservation { unsigned char *stmp = (unsigned char*)malloc(m->codesize+1); unsigned int prev = 0; memset(stmp,UNKNOWN,m->codesize+1); if( !vm->trusted_code && !neko_check_stack(m,stmp,0,0,0) ) { free(stmp); failure("Stack check failed for global scope"); } for(i=0;inglobals;i++) { vfunction *f = (vfunction*)m->globals[i]; if( val_type(f) == VAL_FUNCTION ) { itmp = (unsigned int)(int_val)f->addr; if( itmp >= m->codesize || !tmp[itmp] || itmp < prev ) { free(stmp); ERROR(); } if( !vm->trusted_code && !neko_check_stack(m,stmp,itmp,f->nargs,f->nargs) ) { free(stmp); failure("Stack check failed for function scope"); } f->addr = m->code + itmp; prev = itmp; } } free(stmp); } free(tmp); if( vm->fstats ) vm->fstats(vm,"neko_read_module_check",0); // jit ? if( vm->run_jit ) { if( vm->fstats ) vm->fstats(vm,"neko_read_module_jit",1); neko_module_jit(m); if( vm->fstats ) vm->fstats(vm,"neko_read_module_jit",0); } # ifdef NEKO_DIRECT_THREADED { int_val *jtbl = neko_get_ttable(); if( vm->fstats ) vm->fstats(vm,"neko_read_module_thread",1); for(i=0;i<=m->codesize;i++) { int_val c = m->code[i]; m->code[i] = jtbl[c]; i += parameter_table[c]; } if( vm->fstats ) vm->fstats(vm,"neko_read_module_thread",0); } # endif return m; } int neko_file_reader( readp p, void *buf, int size ) { int len = 0; while( size > 0 ) { int l; POSIX_LABEL(fread_again); l = (int)fread(buf,1,size,(FILE*)p); if( l <= 0 ) { HANDLE_FINTR((FILE*)p,fread_again); return len; } size -= l; len += l; buf = (char*)buf+l; } return len; } int neko_string_reader( readp p, void *buf, int size ) { string_pos *sp = (string_pos*)p; int delta = (sp->len >= size)?size:sp->len; memcpy(buf,sp->p,delta); sp->p += delta; sp->len -= delta; return delta; } /* ************************************************************************ */ neko-2-4-0/vm/neko.h.in000066400000000000000000000337511464615675700146410ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _NEKO_H #define _NEKO_H // OS FLAGS #if defined(_WIN32) # define NEKO_WINDOWS #endif #if defined(__APPLE__) || defined(macintosh) # define NEKO_MAC #endif #if defined(linux) || defined(__linux__) # define NEKO_LINUX #endif #if defined(__FreeBSD_kernel__) # define NEKO_GNUKBSD #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) # define NEKO_BSD #endif #if defined(__GNU__) # define NEKO_HURD #endif // COMPILER/PROCESSOR FLAGS #if defined(__GNUC__) # define NEKO_GCC #endif #if defined(_MSC_VER) # define NEKO_VCC // remove deprecated C API usage warnings # pragma warning( disable : 4996 ) #endif #if defined(__MINGW32__) # define NEKO_MINGW #endif #if defined(__CYGWIN__) # define NEKO_CYGWIN #endif #if defined(__i386__) || defined(_WIN32) # define NEKO_X86 #endif #if defined(__ppc__) # define NEKO_PPC #endif #if defined(_64BITS) || defined(__x86_64__) || defined(_M_AMD64) # define NEKO_64BITS #endif #if defined(NEKO_LINUX) || defined(NEKO_MAC) || defined(NEKO_BSD) || defined(NEKO_GNUKBSD) || defined(NEKO_HURD) || defined(NEKO_CYGWIN) # define NEKO_POSIX #endif #if defined(NEKO_GCC) # define NEKO_THREADED # define NEKO_DIRECT_THREADED #endif #ifndef NEKO_NO_THREADS # define NEKO_THREADS #endif #include #ifndef NEKO_VCC # include #endif #cmakedefine NEKO_XLOCALE_H #cmakedefine NEKO_BIG_ENDIAN #ifndef NEKO_BIG_ENDIAN # define NEKO_LITTLE_ENDIAN #endif #cmakedefine NEKO_JIT_DISABLE #cmakedefine NEKO_JIT_DEBUG #if !defined(NEKO_JIT_DISABLE) && defined(NEKO_X86) && !defined(NEKO_MAC) && !defined(_WIN64) #define NEKO_JIT_ENABLE #endif #define NEKO_VERSION_MAJOR @NEKO_VERSION_MAJOR@ #define NEKO_VERSION_MINOR @NEKO_VERSION_MINOR@ #define NEKO_VERSION_PATCH @NEKO_VERSION_PATCH@ #define NEKO_VERSION @NEKO_VERSION_MAJOR@@NEKO_VERSION_MINOR@@NEKO_VERSION_PATCH@ #define NEKO_BUILD_YEAR @NEKO_BUILD_YEAR@ #define NEKO_MODULE_PATH "@NEKO_MODULE_PATH@" typedef intptr_t int_val; typedef enum { VAL_INT = 0xFF, VAL_NULL = 0, VAL_FLOAT = 1, VAL_BOOL = 2, VAL_STRING = 3, VAL_OBJECT = 4, VAL_ARRAY = 5, VAL_FUNCTION = 6, VAL_ABSTRACT = 7, VAL_INT32 = 8, VAL_PRIMITIVE = 6 | 16, VAL_JITFUN = 6 | 32, VAL_32_BITS = 0xFFFFFFFF } val_type; struct _value { val_type t; }; struct _buffer; typedef int field; typedef struct { int __zero; } *vkind; typedef struct _value *value; typedef struct { field id; value v; } objcell; typedef struct _objtable { int count; objcell *cells; } objtable; typedef struct _buffer *buffer; typedef double tfloat; typedef void (*finalizer)(value v); #pragma pack(4) typedef struct { val_type t; tfloat f; } vfloat; typedef struct { val_type t; int i; } vint32; #pragma pack() typedef struct _vobject { val_type t; objtable table; struct _vobject *proto; } vobject; typedef struct { val_type t; int nargs; void *addr; value env; void *module; } vfunction; typedef struct { val_type t; char c; } vstring; typedef struct { val_type t; value ptr; } varray; typedef struct { val_type t; vkind kind; void *data; } vabstract; typedef struct hcell { int hkey; value key; value val; struct hcell *next; } hcell; typedef struct { hcell **cells; int ncells; int nitems; } vhash; struct _mt_local; struct _mt_lock; typedef struct _mt_local mt_local; typedef struct _mt_lock mt_lock; #define NEKO_TAG_BITS 4 #define val_tag(v) (*(val_type*)(v)) #define val_short_tag(v) (val_tag(v)&((1<data #define val_kind(v) ((vabstract*)(v))->kind #define val_type(v) (val_is_int(v) ? VAL_INT : val_short_tag(v)) #define val_int(v) (((int)(int_val)(v)) >> 1) #define val_float(v) (CONV_FLOAT ((vfloat*)(v))->f) #define val_int32(v) (((vint32*)(v))->i) #define val_any_int(v) (val_is_int(v)?val_int(v):val_int32(v)) #define val_bool(v) ((v) == val_true) #define val_number(v) (val_is_int(v)?val_int(v):((val_tag(v)==VAL_FLOAT)?val_float(v):val_int32(v))) #define val_hdata(v) ((vhash*)val_data(v)) #define val_string(v) (&((vstring*)(v))->c) #define val_strlen(v) ((signed)(((unsigned)val_tag(v)) >> NEKO_TAG_BITS)) #define val_set_length(v,l) val_tag(v) = val_short_tag(v) | ((l) << NEKO_TAG_BITS) #define val_set_size val_set_length #define val_array_size(v) ((signed)(((unsigned)val_tag(v)) >> NEKO_TAG_BITS)) #define val_array_ptr(v) (&((varray*)(v))->ptr) #define val_fun_nargs(v) ((vfunction*)(v))->nargs #define alloc_int(v) ((value)(int_val)((((int)(v)) << 1) | 1)) #define alloc_bool(b) ((b)?val_true:val_false) #define max_array_size ((1 << (32 - NEKO_TAG_BITS)) - 1) #define max_string_size ((1 << (32 - NEKO_TAG_BITS)) - 1) #define invalid_comparison 0xFE #undef EXTERN #undef EXPORT #undef IMPORT #if defined(NEKO_VCC) || defined(NEKO_MINGW) # define INLINE __inline # define EXPORT __declspec( dllexport ) # define IMPORT __declspec( dllimport ) #else # define INLINE inline # define EXPORT # define IMPORT #endif #if defined(NEKO_SOURCES) || defined(NEKO_STANDALONE) # define EXTERN EXPORT #else # define EXTERN IMPORT #endif #define VEXTERN extern EXTERN #ifdef __cplusplus # define C_FUNCTION_BEGIN extern "C" { # define C_FUNCTION_END }; #else # define C_FUNCTION_BEGIN # define C_FUNCTION_END # ifndef true # define true 1 # define false 0 typedef int bool; # endif #endif // the two upper bits must be either 00 or 11 #define need_32_bits(i) ( (((unsigned int)(i)) + 0x40000000) & 0x80000000 ) #define alloc_best_int(i) (need_32_bits(i) ? alloc_int32(i) : alloc_int(i)) #define neko_error() return NULL #define failure(msg) _neko_failure(alloc_string(msg),__FILE__,__LINE__) #define bfailure(buf) _neko_failure(buffer_to_string(buf),__FILE__,__LINE__) #ifndef CONV_FLOAT # define CONV_FLOAT #endif #ifdef NEKO_POSIX # include # define POSIX_LABEL(name) name: # define HANDLE_EINTR(label) if( errno == EINTR ) goto label # define HANDLE_FINTR(f,label) if( ferror(f) && errno == EINTR ) goto label #else # define POSIX_LABEL(name) # define HANDLE_EINTR(label) # define HANDLE_FINTR(f,label) #endif #define VAR_ARGS (-1) #define DEFINE_PRIM_MULT(func) C_FUNCTION_BEGIN EXPORT void *func##__MULT() { return (void*)(&func); } C_FUNCTION_END #define DEFINE_PRIM(func,nargs) C_FUNCTION_BEGIN EXPORT void *func##__##nargs() { return (void*)(&func); } C_FUNCTION_END #define DEFINE_PRIM_WITH_NAME(func,name,nargs) C_FUNCTION_BEGIN EXPORT void *name##__##nargs() { return (void*)(&func); } C_FUNCTION_END #define DEFINE_KIND(name) int_val __kind_##name = 0; vkind name = (vkind)&__kind_##name; #ifdef NEKO_STANDALONE # define DEFINE_ENTRY_POINT(name) #else # define DEFINE_ENTRY_POINT(name) C_FUNCTION_BEGIN void name(); EXPORT void *__neko_entry_point() { return &name; } C_FUNCTION_END #endif #ifdef HEADER_IMPORTS # define H_EXTERN IMPORT #else # define H_EXTERN EXPORT #endif #define DECLARE_PRIM(func,nargs) C_FUNCTION_BEGIN H_EXTERN void *func##__##nargs(); C_FUNCTION_END #define DECLARE_KIND(name) C_FUNCTION_BEGIN H_EXTERN extern vkind name; C_FUNCTION_END #define alloc_int32 neko_alloc_int32 #define alloc_float neko_alloc_float #define alloc_string neko_alloc_string #define alloc_empty_string neko_alloc_empty_string #define copy_string neko_copy_string #define val_this neko_val_this #define val_id neko_val_id #define val_field neko_val_field #define alloc_object neko_alloc_object #define alloc_field neko_alloc_field #define alloc_array neko_alloc_array #define val_call0 neko_val_call0 #define val_call1 neko_val_call1 #define val_call2 neko_val_call2 #define val_call3 neko_val_call3 #define val_callN neko_val_callN #define val_ocall0 neko_val_ocall0 #define val_ocall1 neko_val_ocall1 #define val_ocall2 neko_val_ocall2 #define val_ocallN neko_val_ocallN #define val_callEx neko_val_callEx #define alloc_root neko_alloc_root #define free_root neko_free_root #define alloc neko_alloc #define alloc_private neko_alloc_private #define alloc_abstract neko_alloc_abstract #define alloc_function neko_alloc_function #define alloc_buffer neko_alloc_buffer #define buffer_append neko_buffer_append #define buffer_append_sub neko_buffer_append_sub #define buffer_append_char neko_buffer_append_char #define buffer_length neko_buffer_length #define buffer_to_string neko_buffer_to_string #define val_buffer neko_val_buffer #define val_compare neko_val_compare #define val_print neko_val_print #define val_gc neko_val_gc #define val_throw neko_val_throw #define val_rethrow neko_val_rethrow #define val_iter_fields neko_val_iter_fields #define val_field_name neko_val_field_name #define val_hash neko_val_hash #define k_hash neko_k_hash #define kind_share neko_kind_share #define kind_lookup neko_kind_lookup #define alloc_local neko_alloc_local #define local_get neko_local_get #define local_set neko_local_set #define free_local neko_free_local #define alloc_lock neko_alloc_lock #define lock_acquire neko_lock_acquire #define lock_try neko_lock_try #define lock_release neko_lock_release #define free_lock neko_free_lock C_FUNCTION_BEGIN VEXTERN vkind k_hash; VEXTERN value val_null; VEXTERN value val_true; VEXTERN value val_false; EXTERN value alloc_float( tfloat t ); EXTERN value alloc_int32( int i ); EXTERN value alloc_string( const char *str ); EXTERN value alloc_empty_string( unsigned int size ); EXTERN value copy_string( const char *str, int_val size ); EXTERN value val_this(); EXTERN field val_id( const char *str ); EXTERN value val_field( value o, field f ); EXTERN value alloc_object( value o ); EXTERN void alloc_field( value obj, field f, value v ); EXTERN void val_iter_fields( value obj, void f( value v, field f, void * ), void *p ); EXTERN value val_field_name( field f ); EXTERN value alloc_array( unsigned int n ); EXTERN value alloc_abstract( vkind k, void *data ); EXTERN value val_call0( value f ); EXTERN value val_call1( value f, value arg ); EXTERN value val_call2( value f, value arg1, value arg2 ); EXTERN value val_call3( value f, value arg1, value arg2, value arg3 ); EXTERN value val_callN( value f, value *args, int nargs ); EXTERN value val_ocall0( value o, field f ); EXTERN value val_ocall1( value o, field f, value arg ); EXTERN value val_ocall2( value o, field f, value arg1, value arg2 ); EXTERN value val_ocallN( value o, field f, value *args, int nargs ); EXTERN value val_callEx( value vthis, value f, value *args, int nargs, value *exc ); EXTERN value *alloc_root( unsigned int nvals ); EXTERN void free_root( value *r ); EXTERN char *alloc( unsigned int nbytes ); EXTERN char *alloc_private( unsigned int nbytes ); EXTERN value alloc_function( void *c_prim, unsigned int nargs, const char *name ); EXTERN buffer alloc_buffer( const char *init ); EXTERN void buffer_append( buffer b, const char *s ); EXTERN void buffer_append_sub( buffer b, const char *s, int_val len ); EXTERN void buffer_append_char( buffer b, char c ); EXTERN value buffer_to_string( buffer b ); EXTERN int buffer_length( buffer b ); EXTERN void val_buffer( buffer b, value v ); EXTERN int val_compare( value a, value b ); EXTERN void val_print( value s ); EXTERN void val_gc( value v, finalizer f ); EXTERN void val_throw( value v ); EXTERN void val_rethrow( value v ); EXTERN int val_hash( value v ); EXTERN void kind_share( vkind *k, const char *name ); EXTERN vkind kind_lookup( const char *name ); EXTERN void _neko_failure( value msg, const char *file, int line ); // MULTITHREADING API EXTERN mt_local *alloc_local(); EXTERN void *local_get( mt_local *l ); EXTERN void local_set( mt_local *l, void *v ); EXTERN void free_local( mt_local *l ); EXTERN mt_lock *alloc_lock(); EXTERN void lock_acquire( mt_lock *l ); EXTERN int lock_try( mt_lock *l ); EXTERN void lock_release( mt_lock *l ); EXTERN void free_lock( mt_lock *l ); C_FUNCTION_END #endif /* ************************************************************************ */ neko-2-4-0/vm/neko_elf.h000066400000000000000000000046501464615675700150560ustar00rootroot00000000000000/* * Copyright (C)2016-2017 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _NEKO_ELF_H #define _NEKO_ELF_H #include "neko.h" #ifdef __GNUC__ #ifdef ABI_ELF # define SEPARATE_SECTION_FOR_BYTECODE #endif #endif /* None of this is needed on non-ELF platforms... */ #ifdef SEPARATE_SECTION_FOR_BYTECODE #include #include #define elf_get_Ehdr(p,f) (elf_is_32() ? ((Elf32_Ehdr*)p)->f : ((Elf64_Ehdr*)p)->f) #define elf_get_Shdr(p,f) (elf_is_32() ? ((Elf32_Shdr*)p)->f : ((Elf64_Shdr*)p)->f) #define elf_set_Ehdr(p,f,v) { if (elf_is_32()) ((Elf32_Ehdr*)p)->f = v; else ((Elf64_Ehdr*)p)->f = v; } #define elf_set_Shdr(p,f,v) { if (elf_is_32()) ((Elf32_Shdr*)p)->f = v; else ((Elf64_Shdr*)p)->f = v; } C_FUNCTION_BEGIN VEXTERN int size_Ehdr; /* Big enough to hold Elf32_Ehdr or Elf64_Ehdr... */ VEXTERN int size_Shdr; /* Big enough to hold Elf32_Shdr or Elf64_Shdr... */ EXTERN value elf_read_header(FILE *exe); EXTERN int elf_is_32(); EXTERN value elf_read_section(FILE *exe, int sec, char *buf); EXTERN value elf_write_section(FILE *exe, int sec, char *buf); EXTERN int elf_find_bytecode_section(FILE *exe); EXTERN void elf_free_section_string_table(); EXTERN value elf_find_embedded_bytecode(const char *file, int *beg, int *end); EXTERN int elf_find_bytecode_section(FILE *exe); C_FUNCTION_END #endif #endif /* ************************************************************************ */ neko-2-4-0/vm/neko_mod.h000066400000000000000000000041431464615675700150640ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _NEKO_MOD_H #define _NEKO_MOD_H #include "neko.h" typedef struct _neko_debug { int base; unsigned int bits; } neko_debug; typedef struct _neko_module { void *jit; unsigned int nglobals; unsigned int nfields; unsigned int codesize; value name; value *globals; value *fields; value loader; value exports; value dbgtbl; neko_debug *dbgidxs; int_val *code; value jit_gc; } neko_module; typedef void *readp; typedef int (*reader)( readp p, void *buf, int size ); typedef struct { char *p; int len; } string_pos; C_FUNCTION_BEGIN VEXTERN field neko_id_module; VEXTERN vkind neko_kind_module; EXTERN neko_module *neko_read_module( reader r, readp p, value loader ); EXTERN int neko_file_reader( readp p, void *buf, int size ); // FILE * EXTERN int neko_string_reader( readp p, void *buf, int size ); // string_pos * EXTERN value neko_select_file( value path, const char *file, const char *ext ); C_FUNCTION_END #endif /* ************************************************************************ */ neko-2-4-0/vm/neko_vm.h000066400000000000000000000052611464615675700147310ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _NEKO_VM_H #define _NEKO_VM_H #include "neko.h" typedef void (*neko_printer)( const char *data, int size, void *param ); typedef void (*thread_main_func)( void *param ); typedef struct _neko_vm neko_vm; typedef void (*neko_stat_func)( neko_vm *vm, const char *kind, int start ); C_FUNCTION_BEGIN EXTERN void neko_global_init(); EXTERN void neko_global_free(); EXTERN void neko_gc_major(); EXTERN void neko_gc_loop(); EXTERN void neko_gc_stats( int *heap, int *free ); EXTERN int neko_thread_create( thread_main_func init, thread_main_func main, void *param, void **handle ); EXTERN void neko_thread_blocking( thread_main_func f, void *p ); EXTERN bool neko_thread_register( bool t ); EXTERN neko_vm *neko_vm_alloc( void *unused ); EXTERN neko_vm *neko_vm_current(); EXTERN value neko_exc_stack( neko_vm *vm ); EXTERN value neko_call_stack( neko_vm *vm ); EXTERN void *neko_vm_custom( neko_vm *vm, vkind k ); EXTERN void neko_vm_set_custom( neko_vm *vm, vkind k, void *v ); EXTERN value neko_vm_execute( neko_vm *vm, void *module ); EXTERN void neko_vm_select( neko_vm *vm ); EXTERN int neko_vm_jit( neko_vm *vm, int enable_jit ); EXTERN int neko_vm_trusted( neko_vm *vm, int trusted ); EXTERN value neko_default_loader( char **argv, int argc ); EXTERN void neko_vm_redirect( neko_vm *vm, neko_printer print, void *param ); EXTERN void neko_vm_set_stats( neko_vm *vm, neko_stat_func fstats, neko_stat_func pstats ); EXTERN void neko_vm_dump_stack( neko_vm *vm ); EXTERN int neko_is_big_endian(); C_FUNCTION_END #endif /* ************************************************************************ */ neko-2-4-0/vm/objtable.c000066400000000000000000000056341464615675700150540ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include "objtable.h" int otable_remove( objtable *t, field id ) { int min = 0; int max = t->count; int mid; field cid; objcell *c = t->cells; if( !max ) return 0; while( min < max ) { mid = (min + max) >> 1; cid = c[mid].id; if( cid < id ) min = mid + 1; else if( cid > id ) max = mid; else { t->count--; memmove(&c[mid], &c[mid + 1], (t->count - mid) * sizeof(objcell)); c[t->count].v = val_null; return 1; } } return 0; } void otable_optimize( objtable *t ) { int max = t->count; int i; int cur = 0; objcell *c = t->cells; for(i=0;icount = cur; } void otable_replace( objtable *t, field id, value data ) { int min = 0; int max = t->count; int mid; field cid; objcell *c = t->cells; const size_t objcell_size = sizeof(objcell); while( min < max ) { mid = (min + max) >> 1; cid = c[mid].id; if( cid < id ) min = mid + 1; else if( cid > id ) max = mid; else { c[mid].v = data; return; } } mid = (min + max) >> 1; c = (objcell*)alloc(objcell_size * (t->count + 1)); memcpy(c, t->cells, mid * objcell_size); c[mid].id = id; c[mid].v = data; memcpy(&c[mid + 1], &t->cells[mid], (t->count - mid) * objcell_size); t->cells = c; t->count++; } void otable_copy( objtable *t, objtable *target ) { const size_t size = sizeof(objcell) * t->count; target->count = t->count; target->cells = (objcell*)alloc(size); memcpy(target->cells,t->cells,size); } void otable_iter(objtable *t, void f( value data, field id, void *), void *p ) { int i; const int n = (const int)t->count; objcell *c = t->cells; for(i=0;icount = 0; t->cells = NULL; } static INLINE value *otable_find(objtable *t,field id) { int min; int max; int mid; objcell *c; field cid; min = 0; max = t->count; c = t->cells; while( min < max ) { mid = (min + max) >> 1; cid = c[mid].id; if( cid < id ) min = mid + 1; else if( cid > id ) max = mid; else return &c[mid].v; } return NULL; } static INLINE value otable_get(objtable *t,field id) { int min; int max; int mid; const objcell *c; field cid; min = 0; max = t->count; c = (const objcell*)t->cells; while( min < max ) { mid = (min + max) >> 1; cid = c[mid].id; if( cid < id ) min = mid + 1; else if( cid > id ) max = mid; else return c[mid].v; } return val_null; } void otable_replace(objtable *t, field id, value data); int otable_remove(objtable *t, field id); void otable_optimize(objtable *t); #define otable_count(t) (t)->count void otable_copy(objtable *t, objtable *target); void otable_iter(objtable *t, void f( value data, field id, void *), void *p ); #endif /* ************************************************************************ */ neko-2-4-0/vm/opcodes.h000066400000000000000000000102711464615675700147240ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _NEKO_OPCODES_H #define _NEKO_OPCODES_H #ifndef OP # define OP(x) x # define OPBEGIN enum OPCODE { # define OPEND }; #endif OPBEGIN OP(AccNull), OP(AccTrue), OP(AccFalse), OP(AccThis), OP(AccInt), OP(AccStack), OP(AccGlobal), OP(AccEnv), OP(AccField), OP(AccArray), OP(AccIndex), OP(AccBuiltin), OP(SetStack), OP(SetGlobal), OP(SetEnv), OP(SetField), OP(SetArray), OP(SetIndex), OP(SetThis), OP(Push), OP(Pop), OP(Call), OP(ObjCall), OP(Jump), OP(JumpIf), OP(JumpIfNot), OP(Trap), OP(EndTrap), OP(Ret), OP(MakeEnv), OP(MakeArray), OP(Bool), OP(IsNull), OP(IsNotNull), OP(Add), OP(Sub), OP(Mult), OP(Div), OP(Mod), OP(Shl), OP(Shr), OP(UShr), OP(Or), OP(And), OP(Xor), OP(Eq), OP(Neq), OP(Gt), OP(Gte), OP(Lt), OP(Lte), OP(Not), OP(TypeOf), OP(Compare), OP(Hash), OP(New), OP(JumpTable), OP(Apply), OP(AccStack0), OP(AccStack1), OP(AccIndex0), OP(AccIndex1), OP(PhysCompare), OP(TailCall), OP(Loop), OP(MakeArray2), OP(AccInt32), OP(Last), OPEND #ifdef PARAMETER_TABLE static int parameter_table[] = { 0, // AccNull 0, // AccTrue 0, // AccFalse 0, // AccThis 1, // AccInt 1, // AccStack 1, // AccGlobal 1, // AccEnv 1, // AccField 0, // AccArray 1, // AccIndex 1, // AccBuiltin 1, // SetStack 1, // SetGlobal 1, // SetEnv 1, // SetField 0, // SetArray 1, // SetIndex 0, // SetThis 0, // Push 1, // Pop 1, // Call 1, // ObjCall 1, // Jump 1, // JumpIf 1, // JumpIfNot 1, // Trap 0, // EndTrap 1, // Ret 1, // MakeEnv 1, // MakeArray 0, // Bool 0, // IsNull 0, // IsNotNull 0, // Add 0, // Sub 0, // Mult 0, // Div 0, // Mod 0, // Shl 0, // Shr 0, // UShr 0, // Or 0, // And 0, // Xor 0, // Eq 0, // Neq 0, // Gt 0, // Gte 0, // Lt 0, // Lte 0, // Not 0, // TypeOf 0, // Compare 0, // Hash 0, // New 1, // JumpTable 1, // Apply 0, // AccStack0 0, // AccStack1 0, // AccIndex0 0, // AccIndex1 0, // PhysCompare 1, // TailCall 0, // Loop 1, // MakeArray2 1, // AccInt32 }; #endif #ifdef STACK_TABLE #define P 0xFF static int stack_table[] = { 0, // AccNull 0, // AccTrue 0, // AccFalse 0, // AccThis 0, // AccInt 0, // AccStack 0, // AccGlobal 0, // AccEnv 0, // AccField -1, // AccArray 0, // AccIndex 0, // AccBuiltin 0, // SetStack 0, // SetGlobal 0, // SetEnv -1, // SetField -2, // SetArray -1, // SetIndex 0, // SetThis 1, // Push -P, // Pop -P, // Call -P, // ObjCall 0, // Jump 0, // JumpIf 0, // JumpIfNot 6, // Trap -6, // EndTrap 0, // Ret -P, // MakeEnv -P, // MakeArray 0, // Bool 0, // IsNull 0, // IsNotNull -1, // Add -1, // Sub -1, // Mult -1, // Div -1, // Mod -1, // Shl -1, // Shr -1, // UShr -1, // Or -1, // And -1, // Xor -1, // Eq -1, // Neq -1, // Gt -1, // Gte -1, // Lt -1, // Lte 0, // Not 0, // TypeOf -1, // Compare 0, // Hash 0, // New 0, // JumpTable -P, // Apply 0, // AccStack0 0, // AccStack1 0, // AccIndex0 0, // AccIndex1 -1, // PhysCompare 0, // TailCall 0, // Loop -P, // MakeArray2 0, // AccInt32 0, // Last }; #endif #endif /* ************************************************************************ */ neko-2-4-0/vm/others.c000066400000000000000000000321451464615675700145730ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include "neko.h" #include "objtable.h" #include "vm.h" #define C(x,y) ((x << 8) | y) #define FLOAT_FMT "%.15g" DEFINE_KIND(k_hash); extern mt_lock *neko_fields_lock; extern objtable *neko_fields; extern field id_compare; extern field id_string; extern char *jit_handle_trap; typedef void (*jit_handle)( neko_vm * ); static INLINE int icmp( int a, int b ) { return (a == b)?0:((a < b)?-1:1); } static INLINE int fcmp( tfloat a, tfloat b ) { if(a != a || b != b) return invalid_comparison; return (a == b)?0:((a < b)?-1:1); } static INLINE int scmp( const char *s1, int l1, const char *s2, int l2 ) { int r = memcmp(s1,s2,(l1 < l2)?l1:l2); return r?r:icmp(l1,l2); } EXTERN int val_compare( value a, value b ) { char tmp_buf[32]; switch( C(val_type(a),val_type(b)) ) { case C(VAL_INT,VAL_INT): return icmp(val_int(a),val_int(b)); case C(VAL_INT32,VAL_INT): return icmp(val_int32(a),val_int(b)); case C(VAL_INT,VAL_INT32): return icmp(val_int(a),val_int32(b)); case C(VAL_INT32,VAL_INT32): return icmp(val_int32(a),val_int32(b)); case C(VAL_INT,VAL_FLOAT): return fcmp(val_int(a),val_float(b)); case C(VAL_INT32,VAL_FLOAT): return fcmp(val_int32(a),val_float(b)); case C(VAL_INT,VAL_STRING): return scmp(tmp_buf,sprintf(tmp_buf,"%d",val_int(a)),val_string(b),val_strlen(b)); case C(VAL_INT32,VAL_STRING): return scmp(tmp_buf,sprintf(tmp_buf,"%d",val_int32(a)),val_string(b),val_strlen(b)); case C(VAL_FLOAT,VAL_INT): return fcmp(val_float(a),val_int(b)); case C(VAL_FLOAT,VAL_INT32): return fcmp(val_float(a),val_int32(b)); case C(VAL_FLOAT,VAL_FLOAT): return fcmp(val_float(a),val_float(b)); case C(VAL_FLOAT,VAL_STRING): return scmp(tmp_buf,sprintf(tmp_buf,FLOAT_FMT,val_float(a)),val_string(b),val_strlen(b)); case C(VAL_STRING,VAL_INT): return scmp(val_string(a),val_strlen(a),tmp_buf,sprintf(tmp_buf,"%d",val_int(b))); case C(VAL_STRING,VAL_INT32): return scmp(val_string(a),val_strlen(a),tmp_buf,sprintf(tmp_buf,"%d",val_int32(b))); case C(VAL_STRING,VAL_FLOAT): return scmp(val_string(a),val_strlen(a),tmp_buf,sprintf(tmp_buf,FLOAT_FMT,val_float(b))); case C(VAL_STRING,VAL_BOOL): return scmp(val_string(a),val_strlen(a),val_bool(b)?"true":"false",val_bool(b)?4:5); case C(VAL_BOOL,VAL_STRING): return scmp(val_bool(a)?"true":"false",val_bool(a)?4:5,val_string(b),val_strlen(b)); case C(VAL_STRING,VAL_STRING): return scmp(val_string(a),val_strlen(a),val_string(b),val_strlen(b)); case C(VAL_BOOL,VAL_BOOL): return (a == b) ? 0 : (val_bool(a) ? 1 : -1); case C(VAL_OBJECT,VAL_OBJECT): if( a == b ) return 0; { value tmp = val_field(a,id_compare); if( tmp == val_null ) return invalid_comparison; a = val_callEx(a,tmp,&b,1,NULL); } if( val_is_int(a) ) return val_int(a); return invalid_comparison; default: if( a == b ) return 0; return invalid_comparison; } } typedef struct _stringitem { char *str; int size; int len; struct _stringitem *next; } * stringitem; struct _buffer { int totlen; int blen; stringitem data; }; EXTERN buffer alloc_buffer( const char *init ) { buffer b = (buffer)alloc(sizeof(struct _buffer)); b->totlen = 0; b->blen = 16; b->data = NULL; if( init ) buffer_append(b,init); return b; } static void buffer_append_new( buffer b, const char *s, int len ) { int size; stringitem it; while( b->totlen >= (b->blen << 2) ) b->blen <<= 1; size = (len < b->blen)?b->blen:len; it = (stringitem)alloc(sizeof(struct _stringitem)); it->str = alloc_private(size); memcpy(it->str,s,len); it->size = size; it->len = len; it->next = b->data; b->data = it; } EXTERN void buffer_append_sub( buffer b, const char *s, int_val _len ) { stringitem it; int len = (int)_len; if( s == NULL || len <= 0 ) return; b->totlen += len; it = b->data; if( it ) { int free = it->size - it->len; if( free >= len ) { memcpy(it->str + it->len,s,len); it->len += len; return; } else { memcpy(it->str + it->len,s,free); it->len += free; s += free; len -= free; } } buffer_append_new(b,s,len); } EXTERN void buffer_append( buffer b, const char *s ) { if( s == NULL ) return; buffer_append_sub(b,s,strlen(s)); } EXTERN void buffer_append_char( buffer b, char c ) { stringitem it; b->totlen++; it = b->data; if( it && it->len != it->size ) { it->str[it->len++] = c; return; } buffer_append_new(b,&c,1); } EXTERN value buffer_to_string( buffer b ) { value v = alloc_empty_string(b->totlen); stringitem it = b->data; char *s = (char*)val_string(v) + b->totlen; while( it != NULL ) { stringitem tmp; s -= it->len; memcpy(s,it->str,it->len); tmp = it->next; it = tmp; } return v; } EXTERN int buffer_length( buffer b ) { return b->totlen; } typedef struct vlist { value v; struct vlist *next; } vlist; typedef struct vlist2 { value v; struct vlist *next; buffer b; int prev; } vlist2; static void val_buffer_rec( buffer b, value v, vlist *stack ); static void val_buffer_fields( value v, field f, void *_l ) { vlist2 *l = (vlist2*)_l; if( l->prev ) buffer_append_sub(l->b,", ",2); else { buffer_append_sub(l->b," ",1); l->prev = 1; } val_buffer(l->b,val_field_name(f)); buffer_append_sub(l->b," => ",4); val_buffer_rec(l->b,v,(vlist*)l); } static void val_buffer_rec( buffer b, value v, vlist *stack ) { char buf[32]; int i, l; vlist *vtmp = stack; while( vtmp != NULL ) { if( vtmp->v == v ) { buffer_append_sub(b,"...",3); return; } vtmp = vtmp->next; } switch( val_type(v) ) { case VAL_INT: buffer_append_sub(b,buf,sprintf(buf,"%d",val_int(v))); break; case VAL_STRING: buffer_append_sub(b,val_string(v),val_strlen(v)); break; case VAL_FLOAT: buffer_append_sub(b,buf,sprintf(buf,FLOAT_FMT,val_float(v))); break; case VAL_NULL: buffer_append_sub(b,"null",4); break; case VAL_BOOL: if( val_bool(v) ) buffer_append_sub(b,"true",4); else buffer_append_sub(b,"false",5); break; case VAL_FUNCTION: buffer_append_sub(b,buf,sprintf(buf,"#function:%d",val_fun_nargs(v))); break; case VAL_OBJECT: { value s = val_field(v,id_string); if( s != val_null ) s = val_callEx(v,s,NULL,0,NULL); if( val_is_string(s) ) buffer_append_sub(b,val_string(s),val_strlen(s)); else { vlist2 vtmp; vtmp.v = v; vtmp.next = stack; vtmp.b = b; vtmp.prev = 0; buffer_append_sub(b,"{",1); val_iter_fields(v,val_buffer_fields,&vtmp); if( vtmp.prev ) buffer_append_sub(b," }",2); else buffer_append_sub(b,"}",1); } break; } case VAL_ARRAY: buffer_append_sub(b,"[",1); l = val_array_size(v); { vlist vtmp; vtmp.v = v; vtmp.next = stack; for(i=0;itmp,"%d",x); v = alloc_empty_string(len+len2); if( way ) { memcpy((char*)val_string(v),val_string(str),len); memcpy((char*)val_string(v)+len,vm->tmp,len2+1); } else { memcpy((char*)val_string(v),vm->tmp,len2); memcpy((char*)val_string(v)+len2,val_string(str),len+1); } return v; } value neko_append_strings( value s1, value s2 ) { int len1 = val_strlen(s1); int len2 = val_strlen(s2); value v = alloc_empty_string(len1+len2); memcpy((char*)val_string(v),val_string(s1),len1); memcpy((char*)val_string(v)+len1,val_string(s2),len2+1); return v; } int neko_stack_expand( int_val *sp, int_val *csp, neko_vm *vm ) { int i; int size = (int)((((int_val)vm->spmax - (int_val)vm->spmin) / sizeof(int_val)) << 1); int_val *nsp; if( size > MAX_STACK_SIZE ) { vm->sp = sp; vm->csp = csp; return 0; } nsp = (int_val*)alloc(size * sizeof(int_val)); // csp size i = (int)(((int_val)(csp + 1) - (int_val)vm->spmin) / sizeof(int_val)); memcpy(nsp,vm->spmin,sizeof(int_val) * i); vm->csp = nsp + i - 1; // sp size i = (int)(((int_val)vm->spmax - (int_val)sp) / sizeof(int_val)); memcpy(nsp+size-i,sp,sizeof(int_val) * i); vm->sp = nsp + size - i; vm->spmin = nsp; vm->spmax = nsp + size; return 1; } EXTERN field val_id( const char *name ) { objtable *t; value fdata; field f; value acc = alloc_int(0); const char *oname = name; while( *name ) { acc = alloc_int(223 * val_int(acc) + *((unsigned char*)name)); name++; } f = val_int(acc); t = &neko_fields[f&NEKO_FIELDS_MASK]; fdata = otable_get(t,f); if( fdata == val_null ) { // insert in the table, but by using a larger table that grows faster // since we don't want to resize the table for each insert int min; int max; int mid; field cid; objcell *c; lock_acquire(neko_fields_lock); min = 0; max = t->count; c = t->cells; while( min < max ) { mid = (min + max) >> 1; cid = c[mid].id; if( cid < f ) min = mid + 1; else if( cid > f ) max = mid; else { fdata = c[mid].v; break; } } // in case we found it, it means that it's been inserted by another thread if( fdata == val_null ) { const size_t objcell_size = sizeof(objcell); objcell *c2 = (objcell*)alloc(objcell_size * (t->count + 1)); // copy the whole table mid = (min + max) >> 1; memcpy(c2, c, mid * objcell_size); c2[mid].id = f; c2[mid].v = copy_string(oname,name - oname); memcpy(&c2[mid + 1], &c[mid], (t->count - mid) * objcell_size); // update t->cells = c2; t->count++; } lock_release(neko_fields_lock); } if( fdata != val_null && scmp(val_string(fdata),val_strlen(fdata),oname,(int)(name - oname)) != 0 ) { buffer b = alloc_buffer("Field conflict between "); val_buffer(b,fdata); buffer_append(b," and "); buffer_append(b,oname); bfailure(b); } return f; } EXTERN value val_field_name( field id ) { return otable_get(&neko_fields[id&NEKO_FIELDS_MASK],id); } EXTERN value val_field( value _o, field id ) { value *f; vobject *o = (vobject*)_o; do { f = otable_find(&o->table,id); if( f != NULL ) return *f; o = o->proto; } while( o ); return val_null; } EXTERN void val_iter_fields( value o, void f( value , field, void * ) , void *p ) { otable_iter( &((vobject*)o)->table, f, p ); } EXTERN void val_print( value v ) { neko_vm *vm; if( !val_is_string(v) ) { buffer b = alloc_buffer(NULL); val_buffer(b,v); v = buffer_to_string(b); } vm = NEKO_VM(); vm->print( val_string(v), val_strlen(v), vm->print_param ); } EXTERN void val_throw( value v ) { neko_vm *vm = NEKO_VM(); vm->exc_stack = alloc_array(0); vm->vthis = v; if( *(char**)vm->start == jit_handle_trap ) ((jit_handle)jit_handle_trap)(vm); else longjmp(vm->start,1); } EXTERN void val_rethrow( value v ) { neko_vm *vm = NEKO_VM(); vm->vthis = v; if( *(char**)vm->start == jit_handle_trap ) ((jit_handle)jit_handle_trap)(vm); else longjmp(vm->start,1); } static value failure_to_string() { value o = val_this(); buffer b = alloc_buffer(NULL); val_check(o,object); val_buffer(b,val_field(o,val_id("file"))); buffer_append(b,"("); val_buffer(b,val_field(o,val_id("line"))); buffer_append(b,") : "); val_buffer(b,val_field(o,val_id("msg"))); return buffer_to_string(b); } EXTERN void _neko_failure( value msg, const char *file, int line ) { char *fname = strrchr(file,'/'); char *fname2 = strrchr(file,'\\'); value o = alloc_object(NULL); if( fname2 > fname ) fname = fname2; alloc_field(o,val_id("msg"),msg); alloc_field(o,val_id("file"),alloc_string(fname?(fname+1):file)); alloc_field(o,val_id("line"),alloc_int(line)); alloc_field(o,id_string,alloc_function(failure_to_string,0,"failure_to_string")); val_throw(o); } /* ************************************************************************ */ neko-2-4-0/vm/stats.c000066400000000000000000000125201464615675700144200ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include "neko_vm.h" #ifdef NEKO_WINDOWS # include #else # include #endif typedef struct _statinfos { const char *kind; int ksize; int ncalls; int nerrors; int subtime; int totaltime; int starttime; struct _statinfos *stack; struct _statinfos *next; } statinfos; static statinfos *list = NULL; static statinfos *stack = NULL; static int init_done = 0; static int precise_timer() { # ifdef NEKO_WINDOWS LARGE_INTEGER t; static LARGE_INTEGER freq; if( !init_done ) { DWORD procs, sm, procid = 1; // ensure that we always use the same processor // or else, performance counter might vary depending // on the current CPU GetProcessAffinityMask(GetCurrentProcess(),&procs,&sm); while( !(procs & procid) ) procid <<= 1; SetProcessAffinityMask(GetCurrentProcess(),procid); init_done = 1; QueryPerformanceFrequency(&freq); } QueryPerformanceCounter(&t); return (int)( t.QuadPart * 1000000 / freq.QuadPart ); # else static int base_sec; struct timeval tv; gettimeofday(&tv,NULL); if( !init_done ) { init_done = 1; base_sec = tv.tv_sec; } return (tv.tv_sec - base_sec) * 1000000 + tv.tv_usec; # endif } void neko_stats_measure( neko_vm *vm, const char *kind, int start ) { int ksize = (int)strlen(kind); statinfos *s; if( start ) { int time = precise_timer(); // lookup in list s = list; while( s ) { if( ksize == s->ksize && s->starttime == 0 && memcmp(kind,s->kind,ksize) == 0 ) break; s = s->next; } // init if( s == NULL ) { s = (statinfos*)malloc(sizeof(statinfos)); s->kind = strdup(kind); s->ksize = ksize; s->ncalls = 0; s->nerrors = 0; s->totaltime = 0; s->subtime = 0; s->next = list; list = s; } // add to stack s->ncalls++; s->stack = stack; stack = s; s->starttime = time; } else { // lookup on stack s = stack; while( s ) { statinfos *next; if( ksize == s->ksize && memcmp(kind,s->kind,ksize) == 0 ) break; next = s->stack; s->nerrors++; // stop was not done (exception) s->starttime = 0; s = next; } // pop from stack if( s ) { int delta = precise_timer() - s->starttime; s->totaltime += delta; stack = s->stack; if( stack ) stack->subtime += delta; s->starttime = 0; } else stack = NULL; } } // merged-sort for linked list static int cmp( statinfos *a, statinfos *b ) { int delta = a->totaltime - b->totaltime; if( delta == 0 ) return b->ncalls - a->ncalls; return delta; } static statinfos *sort( statinfos *list ) { statinfos *p, *q, *e, *tail; int insize, nmerges, psize, qsize, i; insize = 1; while( list ) { p = list; list = NULL; tail = NULL; nmerges = 0; while( p ) { nmerges++; q = p; psize = 0; for(i=0;inext; if (!q) break; } qsize = insize; while (psize > 0 || (qsize > 0 && q)) { if( psize == 0 ) { e = q; q = q->next; qsize--; } else if( qsize == 0 || !q ) { e = p; p = p->next; psize--; } else if( cmp(p,q) <= 0 ) { e = p; p = p->next; psize--; } else { e = q; q = q->next; qsize--; } if( tail ) tail->next = e; else list = e; tail = e; } p = q; } tail->next = NULL; if( nmerges <= 1 ) return list; insize *= 2; } return NULL; } value neko_stats_build( neko_vm *vm ) { value v = val_null; statinfos *s = list; // merge duplicates while( s ) { statinfos *s2 = s->next, *prev = s; while( s2 ) { if( s2->ksize == s->ksize && memcmp(s->kind,s2->kind,s->ksize) == 0 ) { s->nerrors += s2->nerrors; s->ncalls += s2->ncalls; s->totaltime += s2->totaltime; s->subtime += s2->subtime; prev->next = s2->next; free(s2); s2 = prev->next; } else { prev = s2; s2 = s2->next; } } s = s->next; } list = sort(list); s = list; while( s ) { value tmp = alloc_array(6); val_array_ptr(tmp)[0] = alloc_string(s->kind); val_array_ptr(tmp)[1] = alloc_int(s->totaltime); val_array_ptr(tmp)[2] = alloc_int(s->totaltime - s->subtime); val_array_ptr(tmp)[3] = alloc_int(s->ncalls); val_array_ptr(tmp)[4] = alloc_int(s->nerrors); val_array_ptr(tmp)[5] = v; v = tmp; s = s->next; } return v; } neko-2-4-0/vm/threads.c000066400000000000000000000206131464615675700147160ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef __APPLE__ // prevent later redefinition of bool # include #endif #include "vm.h" #include #if !defined(NEKO_THREADS) #include struct _mt_local { void *value; }; #else #ifdef NEKO_WINDOWS // necessary for TryEnterCriticalSection // which is only available on 2000 PRO and XP # define _WIN32_WINNT 0x0400 # define GC_NOT_DLL # define GC_WIN32_THREADS #endif #define GC_THREADS #include #if GC_VERSION_MAJOR < 7 # define GC_SUCCESS 0 # define GC_DUPLICATE 1 #endif #ifdef NEKO_WINDOWS struct _mt_lock { CRITICAL_SECTION cs; }; #else #include #include struct _mt_local { pthread_key_t key; }; struct _mt_lock { pthread_mutex_t lock; }; // should be enough to store any GC_stack_base // implementation typedef char __stack_base[64]; #endif #endif // !NEKO_THREADS typedef struct { thread_main_func init; thread_main_func main; void *param; #ifdef NEKO_THREADS # ifdef NEKO_WINDOWS HANDLE lock; # else pthread_mutex_t lock; # endif #endif } tparams; #ifdef NEKO_THREADS #ifdef NEKO_WINDOWS # define THREAD_FUN DWORD WINAPI #else # define THREAD_FUN void * #endif typedef int (*rec)( int, void * ); static int clean_c_stack( int n, void *f ) { char buf[256]; memset(buf,n,sizeof(buf)); if( n == 0 ) return *buf; return ((rec)f)(n-1,f) ? 1 : 0; // prevent tail-rec } static THREAD_FUN ThreadMain( void *_p ) { tparams *lp = (tparams*)_p; tparams p = *lp; p.init(p.param); // we have the 'param' value on this thread C stack // so it's safe to give back control to main thread # ifdef NEKO_WINDOWS ReleaseSemaphore(p.lock,1,NULL); # else pthread_mutex_unlock(&lp->lock); # endif clean_c_stack(40,clean_c_stack); p.main(p.param); return 0; } #endif EXTERN int neko_thread_create( thread_main_func init, thread_main_func main, void *param, void **handle ) { tparams p; p.init = init; p.main = main; p.param = param; # if !defined(NEKO_THREADS) return 0; # elif defined(NEKO_WINDOWS) { HANDLE h; p.lock = CreateSemaphore(NULL,0,1,NULL); h = GC_CreateThread(NULL,0,ThreadMain,&p,0,(void*)handle); if( h == NULL ) { CloseHandle(p.lock); return 0; } WaitForSingleObject(p.lock,INFINITE); CloseHandle(p.lock); CloseHandle(h); return 1; } # else pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); pthread_mutex_init(&p.lock,NULL); pthread_mutex_lock(&p.lock); // force the use of a the GC method to capture created threads // this function should be defined in gc/gc.h if( GC_pthread_create((pthread_t*)handle,&attr,&ThreadMain,&p) != 0 ) { pthread_attr_destroy(&attr); pthread_mutex_destroy(&p.lock); return 0; } pthread_mutex_lock(&p.lock); pthread_attr_destroy(&attr); pthread_mutex_destroy(&p.lock); return 1; # endif } #if defined(NEKO_POSIX) && defined(NEKO_THREADS) # include typedef void (*callb_func)( thread_main_func, void * ); typedef int (*std_func)(); typedef int (*gc_stack_ptr)( __stack_base * ); static int do_nothing( __stack_base *sb ) { return -1; } #endif EXTERN void neko_thread_blocking( thread_main_func f, void *p ) { # if !defined(NEKO_THREADS) f(p); // nothing # elif defined(NEKO_WINDOWS) f(p); // we don't have pthreads issues # else // we have different APIs depending on the GC version, make sure we load // the good one at runtime static callb_func do_blocking = NULL; static std_func start = NULL, end = NULL; if( do_blocking ) do_blocking(f,p); else if( start ) { start(); f(p); end(); } else { void *self = dlopen(NULL,0); do_blocking = (callb_func)dlsym(self,"GC_do_blocking"); if( !do_blocking ) { start = (std_func)dlsym(self,"GC_start_blocking"); end = (std_func)dlsym(self,"GC_end_blocking"); if( !start || !end ) val_throw(alloc_string("Could not init GC blocking API")); } neko_thread_blocking(f,p); } # endif } EXTERN bool neko_thread_register( bool t ) { # if !defined(NEKO_THREADS) return 0; # elif defined(NEKO_WINDOWS) struct GC_stack_base sb; int r; if( !t ) return GC_unregister_my_thread() == GC_SUCCESS; if( GC_get_stack_base(&sb) != GC_SUCCESS ) return 0; r = GC_register_my_thread(&sb); return( r == GC_SUCCESS || r == GC_DUPLICATE ); # else // since the API is only available on GC 7.0, // we will do our best to locate it dynamically static gc_stack_ptr get_sb = NULL, my_thread = NULL; static std_func unreg_my_thread = NULL; if( !t && unreg_my_thread != NULL ) { return unreg_my_thread() == GC_SUCCESS; } else if( my_thread != NULL ) { __stack_base sb; int r; if( get_sb(&sb) != GC_SUCCESS ) return 0; r = my_thread(&sb); return( r == GC_SUCCESS || r == GC_DUPLICATE ); } else { void *self = dlopen(NULL,0); my_thread = (gc_stack_ptr)dlsym(self,"GC_register_my_thread"); get_sb = (gc_stack_ptr)dlsym(self,"GC_get_stack_base"); unreg_my_thread = (std_func)dlsym(self,"GC_unregister_my_thread"); if( my_thread == NULL ) my_thread = do_nothing; if( get_sb == NULL ) get_sb = do_nothing; if( unreg_my_thread == NULL ) unreg_my_thread = (std_func)do_nothing; return neko_thread_register(t); } # endif } EXTERN mt_local *alloc_local() { # if !defined(NEKO_THREADS) mt_local *l = malloc(sizeof(mt_local)); l->value = NULL; return l; # elif defined(NEKO_WINDOWS) DWORD t = TlsAlloc(); TlsSetValue(t,NULL); return (mt_local*)(int_val)t; # else mt_local *l = malloc(sizeof(mt_local)); pthread_key_create(&l->key,NULL); return l; # endif } EXTERN void free_local( mt_local *l ) { # if !defined(NEKO_THREADS) free(l); # elif defined(NEKO_WINDOWS) TlsFree((DWORD)(int_val)l); # else pthread_key_delete(l->key); free(l); # endif } EXTERN void local_set( mt_local *l, void *v ) { # if !defined(NEKO_THREADS) l->value = v; # elif defined(NEKO_WINDOWS) TlsSetValue((DWORD)(int_val)l,v); # else pthread_setspecific(l->key,v); # endif } EXTERN void *local_get( mt_local *l ) { if( l == NULL ) return NULL; # if !defined(NEKO_THREADS) return l->value; # elif defined(NEKO_WINDOWS) return (void*)TlsGetValue((DWORD)(int_val)l); # else return pthread_getspecific(l->key); # endif } EXTERN mt_lock *alloc_lock() { # if !defined(NEKO_THREADS) return (mt_lock*)1; # elif defined(NEKO_WINDOWS) mt_lock *l = (mt_lock*)malloc(sizeof(mt_lock)); InitializeCriticalSection(&l->cs); return l; # else mt_lock *l = (mt_lock*)malloc(sizeof(mt_lock)); pthread_mutexattr_t a; pthread_mutexattr_init(&a); pthread_mutexattr_settype(&a,PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&l->lock,&a); pthread_mutexattr_destroy(&a); return l; # endif } EXTERN void lock_acquire( mt_lock *l ) { # if !defined(NEKO_THREADS) # elif defined(NEKO_WINDOWS) EnterCriticalSection(&l->cs); # else pthread_mutex_lock(&l->lock); # endif } EXTERN int lock_try( mt_lock *l ) { #if !defined(NEKO_THREADS) return 1; # elif defined(NEKO_WINDOWS) return TryEnterCriticalSection(&l->cs); # else return pthread_mutex_trylock(&l->lock) == 0; # endif } EXTERN void lock_release( mt_lock *l ) { # if !defined(NEKO_THREADS) # elif defined(NEKO_WINDOWS) LeaveCriticalSection(&l->cs); # else pthread_mutex_unlock(&l->lock); # endif } EXTERN void free_lock( mt_lock *l ) { # if !defined(NEKO_THREADS) # elif defined(NEKO_WINDOWS) DeleteCriticalSection(&l->cs); free(l); # else pthread_mutex_destroy(&l->lock); free(l); # endif } /* ************************************************************************ */ neko-2-4-0/vm/vm.h000066400000000000000000000043151464615675700137140ustar00rootroot00000000000000/* * Copyright (C)2005-2022 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _NEKO_VMCONTEXT_H #define _NEKO_VMCONTEXT_H #include #include "neko_vm.h" #define INIT_STACK_SIZE (1 << 8) #define MAX_STACK_SIZE (1 << 18) #define MAX_STACK_PER_FUNCTION 128 #define PROF_SIZE (1 << 20) #define CALL_MAX_ARGS 5 #define NEKO_FIELDS_MASK 63 typedef struct _custom_list { vkind tag; void *custom; struct _custom_list *next; } custom_list; struct _neko_vm { int_val *sp; int_val *csp; value env; value vthis; int_val *spmin; int_val *spmax; int_val trap; void *jit_val; jmp_buf start; void *c_stack_max; int run_jit; value exc_stack; neko_printer print; void *print_param; custom_list *clist; value resolver; char tmp[100]; int trusted_code; neko_stat_func fstats; neko_stat_func pstats; }; extern int_val *callback_return; extern mt_local *neko_vm_context; #define NEKO_VM() ((neko_vm*)local_get(neko_vm_context)) extern value neko_alloc_apply( int nargs, value env ); extern value neko_interp( neko_vm *vm, void *m, int_val acc, int_val *pc ); extern int_val *neko_get_ttable(); #endif /* ************************************************************************ */