diff --git a/.github/workflows/cmake-tests.yml b/.github/workflows/cmake-tests.yml index 4fd0c0ae..1e5ec278 100644 --- a/.github/workflows/cmake-tests.yml +++ b/.github/workflows/cmake-tests.yml @@ -69,6 +69,43 @@ jobs: working-directory: ${{github.workspace}}/build run: ./tests + build_and_test_windows: + runs-on: windows-2022 + timeout-minutes: 180 + env: + BUILD_TYPE: RelWithDebInfo + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: MSVC environment + uses: ilammy/msvc-dev-cmd@v1 + + - name: Setup LLVM + uses: ZhongRuoyu/setup-llvm@v0 + with: + llvm-version: 20 + + - name: Install Ninja + run: choco install ninja --yes --no-progress + + - name: Configure CMake + shell: pwsh + run: | + cmake -B build -G Ninja ` + "-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}" ` + "-DCMAKE_C_COMPILER=$env:LLVM_PATH\bin\clang.exe" ` + "-DCMAKE_CXX_COMPILER=$env:LLVM_PATH\bin\clang++.exe" ` + "-DLLVM_DIR=$env:LLVM_PATH\lib\cmake\llvm" + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{ env.BUILD_TYPE }} --target tests + + - name: Test + working-directory: ${{github.workspace}}/build + run: .\tests.exe + # the version the release binaries are stamped with and the tag the release gets. Resolved once, so # both platforms and the release itself cannot disagree about what was built # @@ -77,7 +114,7 @@ jobs: # and no commit pushed back onto master resolve_version: runs-on: ubuntu-24.04 - needs: build_and_test + needs: [build_and_test, build_and_test_windows] outputs: version: ${{ steps.resolve.outputs.version }} patch: ${{ steps.resolve.outputs.patch }} @@ -416,11 +453,60 @@ jobs: name: ${{env.EPM_BIN_NAME}} path: ${{github.workspace}}/${{env.EPM_BIN_NAME}}.tar.gz + build_release_windows_x86_64: + runs-on: windows-2022 + timeout-minutes: 180 + needs: resolve_version + env: + BUILD_TYPE: Release + BIN_NAME: echo-windows-x86_64 + EPM_BIN_NAME: epm-windows-x86_64 + VERSION: ${{needs.resolve_version.outputs.version}}${{needs.resolve_version.outputs.suffix}} + ECO_VERSION_PATCH: ${{needs.resolve_version.outputs.patch}} + ECO_VERSION_SUFFIX: ${{needs.resolve_version.outputs.suffix}} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: MSVC environment + uses: ilammy/msvc-dev-cmd@v1 + + - name: Setup LLVM + uses: ZhongRuoyu/setup-llvm@v0 + with: + llvm-version: 20 + + - name: Install Ninja and Inno Setup + run: choco install ninja innosetup --yes --no-progress + + - name: Build the Windows release + shell: pwsh + run: ./tools/windows/build_release.ps1 + + - name: Upload release archive + uses: actions/upload-artifact@v4 + with: + name: ${{env.BIN_NAME}} + path: ${{github.workspace}}/${{env.BIN_NAME}}.zip + + - name: Upload epm archive + uses: actions/upload-artifact@v4 + with: + name: ${{env.EPM_BIN_NAME}} + path: ${{github.workspace}}/${{env.EPM_BIN_NAME}}.zip + + - name: Upload setup wizard + uses: actions/upload-artifact@v4 + with: + name: echo-windows-x86_64-setup + path: ${{github.workspace}}/echo-windows-x86_64-setup.exe + # every push to master is a patch release. `gh release create` creates the tag itself, so nothing # here pushes a ref by hand and nothing commits back onto the branch release: runs-on: ubuntu-24.04 - needs: [resolve_version, build_release, build_release_macos_arm64] + needs: [resolve_version, build_release, build_release_macos_arm64, build_release_windows_x86_64] if: github.ref == 'refs/heads/master' && github.event_name == 'push' steps: @@ -455,5 +541,8 @@ jobs: --generate-notes \ artifacts/echo-linux-x86_64/echo-linux-x86_64.tar.gz \ artifacts/echo-macos-arm64/echo-macos-arm64.tar.gz \ + artifacts/echo-windows-x86_64/echo-windows-x86_64.zip \ artifacts/epm-linux-x86_64/epm-linux-x86_64.tar.gz \ - artifacts/epm-macos-arm64/epm-macos-arm64.tar.gz + artifacts/epm-macos-arm64/epm-macos-arm64.tar.gz \ + artifacts/epm-windows-x86_64/epm-windows-x86_64.zip \ + artifacts/echo-windows-x86_64-setup/echo-windows-x86_64-setup.exe diff --git a/CMakeLists.txt b/CMakeLists.txt index ce559cb8..1cfd2b4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,20 @@ set(LIBNAME ${APPNAME}_lib) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -g") -set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address") - -set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") +# ASan and the GCC-style debug flags are a Unix host's. MSVC (and clang-cl) reject +# `-fsanitize=address` / `-O0 -g` as unknown options, and a Debug configure on +# Windows would otherwise fail before compiling a single file. clang++ with the +# GNU driver still takes them; it is `MSVC` that is the split, not `WIN32`. +if(NOT MSVC) + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -fsanitize=address") + set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address") +endif() +# MSVC otherwise reads this tree as the system ANSI code page, so a UTF-8 box-drawing literal in +# CommandLineHelp.cpp is three CP1252 characters rather than one glyph. clang defaults to UTF-8; +# this is the flag that makes cl.exe match. directory-wide so a target added later cannot forget it +if(MSVC) + add_compile_options(/utf-8) +endif() # set(CMAKE_BUILD_TYPE Debug) set(VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/") @@ -26,6 +36,50 @@ message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") add_definitions(${LLVM_DEFINITIONS}) include_directories(${LLVM_INCLUDE_DIRS}) +# Official LLVM Windows packages record the DIA SDK from the machine that built +# them - VS Enterprise on the release builders. That path is a file-level Ninja +# input, so a Community/Professional install fails at build time with "missing +# and no known rule" rather than a link error. Rewrite it onto whichever 2022 +# edition actually has diaguids.lib here. +if(WIN32) + unset(ECO_DIA_GUIDS) + foreach(ECO_VS_EDITION Enterprise Professional Community BuildTools) + set(ECO_DIA_CANDIDATE "C:/Program Files/Microsoft Visual Studio/2022/${ECO_VS_EDITION}/DIA SDK/lib/amd64/diaguids.lib") + if(EXISTS "${ECO_DIA_CANDIDATE}") + set(ECO_DIA_GUIDS "${ECO_DIA_CANDIDATE}") + break() + endif() + endforeach() + + if(ECO_DIA_GUIDS) + foreach(ECO_LLVM_LIB IN LISTS LLVM_AVAILABLE_LIBS) + if(NOT TARGET ${ECO_LLVM_LIB}) + continue() + endif() + + get_target_property(ECO_LLVM_LIB_DEPS ${ECO_LLVM_LIB} INTERFACE_LINK_LIBRARIES) + if(NOT ECO_LLVM_LIB_DEPS) + continue() + endif() + + set(ECO_LLVM_LIB_NEW_DEPS "") + set(ECO_LLVM_LIB_REPLACED FALSE) + foreach(ECO_DEP IN LISTS ECO_LLVM_LIB_DEPS) + if(ECO_DEP MATCHES "DIA SDK/.*/diaguids\\.lib") + list(APPEND ECO_LLVM_LIB_NEW_DEPS "${ECO_DIA_GUIDS}") + set(ECO_LLVM_LIB_REPLACED TRUE) + else() + list(APPEND ECO_LLVM_LIB_NEW_DEPS "${ECO_DEP}") + endif() + endforeach() + + if(ECO_LLVM_LIB_REPLACED) + set_property(TARGET ${ECO_LLVM_LIB} PROPERTY INTERFACE_LINK_LIBRARIES "${ECO_LLVM_LIB_NEW_DEPS}") + endif() + endforeach() + endif() +endif() + # DO NOT INCLUDE! THIS CAUSED AN ERROR THAT TOOK ME 3 FULL DAYS TO PIN DOWN # I leave this here as a warning to future me and as a totem to ward off evil spirits # honestly I do not even rember why I added this in the first place, it in combination @@ -35,10 +89,18 @@ include_directories(${LLVM_INCLUDE_DIRS}) # Echo file(GLOB_RECURSE SOURCES "src/*.cpp") -set_source_files_properties(${SOURCES} PROPERTIES COMPILE_FLAGS "-Wall -Wextra -pedantic -Wno-unused-parameter") +if(NOT MSVC) + set_source_files_properties(${SOURCES} PROPERTIES COMPILE_FLAGS "-Wall -Wextra -pedantic -Wno-unused-parameter") +endif() + +# the library is what the tests link. `main.cpp` is the driver, and two `main`s in one +# image is lld picking the compiler's: Catch2 never runs and `--list-tests` is refused +# as an echoc flag. the executable still compiles the driver in +set(LIB_SOURCES ${SOURCES}) +list(FILTER LIB_SOURCES EXCLUDE REGEX "[\\/]main\\.cpp$") add_executable(${APPNAME} ${SOURCES}) -add_library(${LIBNAME} STATIC ${SOURCES}) +add_library(${LIBNAME} STATIC ${LIB_SOURCES}) # where the stdlib sources live. PRIVATE on each target that needs it rather than PUBLIC on the # library, because a consumer of echoc_lib is not thereby a consumer of the source tree - so the three @@ -48,6 +110,25 @@ set(ECO_STDLIB_DEFINITION "STDLIB_SOURCE_DIR=\"${CMAKE_SOURCE_DIR}/stdlib\"") target_compile_definitions(${APPNAME} PRIVATE ${ECO_STDLIB_DEFINITION}) target_compile_definitions(${LIBNAME} PRIVATE ${ECO_STDLIB_DEFINITION}) +# the clang that compiled this binary. `echoc build` / `echoc test` must not +# pick a GNU-ABI clang that happens to sit first on PATH (llvm-mingw cannot +# link the MSVC objects we emit) +set(ECO_HOST_CLANG "${CMAKE_C_COMPILER}") +if(ECO_HOST_CLANG MATCHES "clang\\+\\+") + get_filename_component(ECO_HOST_CLANG_DIR "${ECO_HOST_CLANG}" DIRECTORY) + if(WIN32) + set(ECO_HOST_CLANG_CANDIDATE "${ECO_HOST_CLANG_DIR}/clang.exe") + else() + set(ECO_HOST_CLANG_CANDIDATE "${ECO_HOST_CLANG_DIR}/clang") + endif() + if(EXISTS "${ECO_HOST_CLANG_CANDIDATE}") + set(ECO_HOST_CLANG "${ECO_HOST_CLANG_CANDIDATE}") + endif() +endif() +file(TO_CMAKE_PATH "${ECO_HOST_CLANG}" ECO_HOST_CLANG_DEF) +target_compile_definitions(${APPNAME} PRIVATE "ECO_HOST_CLANG=\"${ECO_HOST_CLANG_DEF}\"") +target_compile_definitions(${LIBNAME} PRIVATE "ECO_HOST_CLANG=\"${ECO_HOST_CLANG_DEF}\"") + # what a *released* binary is built with. the path above is the build machine's, so a binary someone # downloaded can only find the standard library if it carries it - see the comment on # ECO_USE_EMBEDDED_STDLIB in include/eco.h. Off by default: a local build wants the stdlib it can edit, @@ -143,14 +224,18 @@ set(LLVM_COMPONENTS ) # add the native architecture to the list of components -if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") +# +# Windows reports AMD64 where Unix says x86_64. compared case-insensitively so +# a generator that spells `AMD64` and one that spells `amd64` are the same row +string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" ECO_PROCESSOR) +if (ECO_PROCESSOR MATCHES "x86_64|amd64") set(NATIVE_ARCH "X86") -elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64") +elseif (ECO_PROCESSOR MATCHES "arm64|aarch64") # Darwin says arm64, Linux says aarch64. both are AArch64 to LLVM set(NATIVE_ARCH "AArch64") -elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "arm") +elseif (ECO_PROCESSOR MATCHES "arm") set(NATIVE_ARCH "ARM") -elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "riscv64") +elseif (ECO_PROCESSOR MATCHES "riscv64") set(NATIVE_ARCH "RISCV") else() message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}") @@ -225,4 +310,9 @@ target_compile_definitions(tests PRIVATE enable_testing() list(APPEND CMAKE_MODULE_PATH ${Catch2_SOURCE_DIR}/extras) include(Catch) -catch_discover_tests(tests) +# put the LLVM we built against first on PATH for each ctest invocation. Catch2 +# passes TEST_CASE names as argv, so a name starting with `-` is a Catch2 flag +# (`-o` writes a reporter file and then runs the *entire* suite, `--help` prints +# usage). those cases are named with backticks instead, e.g. `` `-o` overrides `` +get_filename_component(ECO_LLVM_BIN "${ECO_HOST_CLANG}" DIRECTORY) +catch_discover_tests(tests DL_PATHS "${ECO_LLVM_BIN}") diff --git a/README.md b/README.md index 72f1173e..7d9dfd89 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,18 @@ Echo won't run PHP right now, nor in the future. Echo is fundamentally different ## Installation +macOS / Linux: + ```bash curl -fsSL https://raw.githubusercontent.com/echolang/echo/master/install.sh | bash ``` -`echoc run hello.eco` to jit stuff. +Windows (PowerShell): + +```powershell +irm https://raw.githubusercontent.com/echolang/echo/master/install.ps1 | iex +``` + +Or download `echo-windows-x86_64-setup.exe` from the [latest release](https://github.com/echolang/echo/releases/latest) and run the wizard. The zip is there if you want to unpack it yourself. -Compiling to a native `echoc build` needs `clang` on your `PATH`; on macOS that means the Xcode command line tools (`xcode-select --install`). +`echoc run hello.eco` to jit. `echoc build hello.eco` emits a native binary. On macOS that needs the Xcode command line tools (`xcode-select --install`). On Windows the installer already ships clang, lld-link and a sysroot; no separate LLVM install. diff --git a/epm/README.md b/epm/README.md index 6fd055f9..87deb227 100644 --- a/epm/README.md +++ b/epm/README.md @@ -4,7 +4,7 @@ A package manager for Echo. It resolves, fetches and vendors. It never compiles anything. It writes a `module.eco` only as a whole new file (`epm init`) or as the one `#[requires:]` line `epm add` was asked for. -A released `epm` arrives next to `echoc` from the install one-liner. This tree +A released `epm` arrives next to `echoc` from the installer. This tree is the bootstrap: path-depends on `../../echolibs/libjson` and `../../echolibs/libcurl`, compiled by this tree's `echoc`. diff --git a/epm/c/common.c b/epm/c/common.c new file mode 100644 index 00000000..17b502d6 --- /dev/null +++ b/epm/c/common.c @@ -0,0 +1,250 @@ +/* + * Platform-independent bits of the epm shim: argv builder, allocator, SHA-256. + */ + +#include "internal.h" + +#include +#include +#include + +void *epm_argv_new(void) +{ + return calloc(1, sizeof(epm_argv)); +} + +int32_t epm_argv_push(void *handle, const char *s) +{ + epm_argv *a = handle; + + if (a == NULL || s == NULL) { + return -1; + } + + if (a->n + 1 >= a->cap) { + int32_t cap = a->cap < 8 ? 8 : a->cap * 2; + char **grown = realloc(a->v, (size_t)cap * sizeof(char *)); + + if (grown == NULL) { + return -1; + } + + a->v = grown; + a->cap = cap; + } + +#ifdef _WIN32 + char *copy = _strdup(s); +#else + char *copy = strdup(s); +#endif + + if (copy == NULL) { + return -1; + } + + a->v[a->n] = copy; + a->n += 1; + a->v[a->n] = NULL; + return 0; +} + +void epm_argv_free(void *handle) +{ + epm_argv *a = handle; + + if (a == NULL) { + return; + } + + int32_t i; + + for (i = 0; i < a->n; i++) { + free(a->v[i]); + } + + free(a->v); + free(a); +} + +int epm_grow(char **buf, size_t *cap, size_t need) +{ + if (need <= *cap) { + return 0; + } + + size_t next = *cap < 256 ? 256 : *cap; + + while (next < need) { + next *= 2; + } + + char *grown = realloc(*buf, next); + + if (grown == NULL) { + return -1; + } + + *buf = grown; + *cap = next; + return 0; +} + +void epm_free(void *p) +{ + free(p); +} + +typedef struct { + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[64]; + size_t fill; +} epm_sha256_ctx; + +static uint32_t rotr(uint32_t x, uint32_t n) +{ + return (x >> n) | (x << (32 - n)); +} + +static void sha256_transform(epm_sha256_ctx *ctx, const uint8_t *block) +{ + static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + uint32_t w[64]; + uint32_t a, b, c, d, e, f, g, h; + size_t i; + + for (i = 0; i < 16; i++) { + w[i] = ((uint32_t)block[i * 4] << 24) + | ((uint32_t)block[i * 4 + 1] << 16) + | ((uint32_t)block[i * 4 + 2] << 8) + | ((uint32_t)block[i * 4 + 3]); + } + + for (i = 16; i < 64; i++) { + uint32_t s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >> 3); + uint32_t s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >> 10); + w[i] = w[i - 16] + s0 + w[i - 7] + s1; + } + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; i++) { + uint32_t S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25); + uint32_t ch = (e & f) ^ ((~e) & g); + uint32_t t1 = h + S1 + ch + K[i] + w[i]; + uint32_t S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); + uint32_t maj = (a & b) ^ (a & c) ^ (b & c); + uint32_t t2 = S0 + maj; + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void *epm_sha256_new(void) +{ + epm_sha256_ctx *ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; + ctx->bitcount = 0; + ctx->fill = 0; + return ctx; +} + +void epm_sha256_update(void *handle, const uint8_t *data, uint64_t n) +{ + epm_sha256_ctx *ctx = handle; + size_t i = 0; + + ctx->bitcount += n * 8; + + while (i < n) { + size_t room = 64 - ctx->fill; + size_t take = (n - i) < room ? (size_t)(n - i) : room; + memcpy(ctx->buffer + ctx->fill, data + i, take); + ctx->fill += take; + i += take; + + if (ctx->fill == 64) { + sha256_transform(ctx, ctx->buffer); + ctx->fill = 0; + } + } +} + +void epm_sha256_final(void *handle, uint8_t *out) +{ + epm_sha256_ctx *ctx = handle; + uint64_t bits = ctx->bitcount; + size_t i; + + ctx->buffer[ctx->fill++] = 0x80; + + if (ctx->fill > 56) { + while (ctx->fill < 64) { + ctx->buffer[ctx->fill++] = 0; + } + sha256_transform(ctx, ctx->buffer); + ctx->fill = 0; + } + + while (ctx->fill < 56) { + ctx->buffer[ctx->fill++] = 0; + } + + for (i = 0; i < 8; i++) { + ctx->buffer[63 - i] = (uint8_t)(bits >> (i * 8)); + } + sha256_transform(ctx, ctx->buffer); + + for (i = 0; i < 8; i++) { + out[i * 4] = (uint8_t)(ctx->state[i] >> 24); + out[i * 4 + 1] = (uint8_t)(ctx->state[i] >> 16); + out[i * 4 + 2] = (uint8_t)(ctx->state[i] >> 8); + out[i * 4 + 3] = (uint8_t)(ctx->state[i]); + } + + free(ctx); +} diff --git a/epm/c/internal.h b/epm/c/internal.h new file mode 100644 index 00000000..c0155371 --- /dev/null +++ b/epm/c/internal.h @@ -0,0 +1,19 @@ +/* + * Shared by the POSIX and Windows process runners. Not part of the Echo FFI. + */ + +#ifndef EPM_SHIM_INTERNAL_H +#define EPM_SHIM_INTERNAL_H + +#include +#include + +typedef struct { + char **v; + int32_t n; + int32_t cap; +} epm_argv; + +int epm_grow(char **buf, size_t *cap, size_t need); + +#endif diff --git a/epm/c/posix.c b/epm/c/posix.c new file mode 100644 index 00000000..b5c57cde --- /dev/null +++ b/epm/c/posix.c @@ -0,0 +1,277 @@ +/* + * POSIX half of the epm shim: directory, stat, mkdir, a process runner. + */ + +#define _POSIX_C_SOURCE 200809L + +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int32_t epm_isatty(int32_t fd) +{ + return isatty(fd) ? 1 : 0; +} + +int32_t epm_columns(int32_t fd) +{ + if (!isatty(fd)) { + return 0; + } + + struct winsize size; + + if (ioctl(fd, TIOCGWINSZ, &size) == 0 && size.ws_col > 0) { + return (int32_t)size.ws_col; + } + + return 0; +} + +DIR *epm_opendir(const char *path) +{ + return opendir(path); +} + +int32_t epm_readdir(DIR *dir, char *name, uint64_t cap) +{ + if (dir == NULL || name == NULL || cap == 0) { + return -1; + } + + for (;;) { + errno = 0; + struct dirent *ent = readdir(dir); + + if (ent == NULL) { + return errno == 0 ? 0 : -1; + } + + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { + continue; + } + + size_t n = strlen(ent->d_name); + if (n + 1 > cap) { + return -1; + } + + memcpy(name, ent->d_name, n + 1); + return 1; + } +} + +void epm_closedir(DIR *dir) +{ + if (dir != NULL) { + closedir(dir); + } +} + +int32_t epm_kind(const char *path) +{ + struct stat st; + + if (stat(path, &st) != 0) { + return errno == ENOENT ? 0 : -1; + } + + return S_ISDIR(st.st_mode) ? 2 : 1; +} + +int32_t epm_mkdir(const char *path) +{ + if (mkdir(path, 0755) == 0) { + return 0; + } + + return errno == EEXIST ? 0 : -1; +} + +int32_t epm_rmdir(const char *path) +{ + return rmdir(path) == 0 ? 0 : -1; +} + +int32_t epm_rename(const char *from, const char *to) +{ + if (from == NULL || to == NULL) { + return -1; + } + + return rename(from, to) == 0 ? 0 : -1; +} + +static void drain_fail(int out_r, int err_r, pid_t pid, char *out_buf, char *err_buf) +{ + close(out_r); + close(err_r); + free(out_buf); + free(err_buf); + waitpid(pid, NULL, 0); +} + +int32_t epm_run( + void *handle, + char **out_s, int32_t *out_n, + char **err_s, int32_t *err_n, + int32_t *code +) +{ + epm_argv *a = handle; + + if (a == NULL || a->n == 0 || a->v == NULL || a->v[0] == NULL) { + return -1; + } + + int out_pipe[2]; + int err_pipe[2]; + + if (pipe(out_pipe) != 0) { + return -1; + } + + if (pipe(err_pipe) != 0) { + close(out_pipe[0]); + close(out_pipe[1]); + return -1; + } + + pid_t pid = fork(); + + if (pid < 0) { + close(out_pipe[0]); + close(out_pipe[1]); + close(err_pipe[0]); + close(err_pipe[1]); + return -1; + } + + if (pid == 0) { + dup2(out_pipe[1], STDOUT_FILENO); + dup2(err_pipe[1], STDERR_FILENO); + close(out_pipe[0]); + close(out_pipe[1]); + close(err_pipe[0]); + close(err_pipe[1]); + execvp(a->v[0], a->v); + _exit(127); + } + + close(out_pipe[1]); + close(err_pipe[1]); + + size_t out_cap = 256; + size_t err_cap = 256; + size_t out_len = 0; + size_t err_len = 0; + char *out_buf = malloc(out_cap); + char *err_buf = malloc(err_cap); + int out_eof = 0; + int err_eof = 0; + + if (out_buf == NULL || err_buf == NULL) { + drain_fail(out_pipe[0], err_pipe[0], pid, out_buf, err_buf); + return -1; + } + + while (!out_eof || !err_eof) { + struct pollfd fds[2]; + nfds_t nfd = 0; + int out_i = -1; + int err_i = -1; + + if (!out_eof) { + fds[nfd].fd = out_pipe[0]; + fds[nfd].events = POLLIN; + out_i = (int)nfd; + nfd += 1; + } + + if (!err_eof) { + fds[nfd].fd = err_pipe[0]; + fds[nfd].events = POLLIN; + err_i = (int)nfd; + nfd += 1; + } + + int pr = poll(fds, nfd, -1); + + if (pr < 0) { + if (errno == EINTR) { + continue; + } + + drain_fail(out_pipe[0], err_pipe[0], pid, out_buf, err_buf); + return -1; + } + + if (out_i >= 0 && (fds[out_i].revents & (POLLIN | POLLHUP | POLLERR))) { + char tmp[512]; + ssize_t n = read(out_pipe[0], tmp, sizeof(tmp)); + + if (n > 0) { + if (epm_grow(&out_buf, &out_cap, out_len + (size_t)n) != 0) { + drain_fail(out_pipe[0], err_pipe[0], pid, out_buf, err_buf); + return -1; + } + + memcpy(out_buf + out_len, tmp, (size_t)n); + out_len += (size_t)n; + } + else if (n == 0 || (n < 0 && errno != EINTR)) { + out_eof = 1; + } + } + + if (err_i >= 0 && (fds[err_i].revents & (POLLIN | POLLHUP | POLLERR))) { + char tmp[512]; + ssize_t n = read(err_pipe[0], tmp, sizeof(tmp)); + + if (n > 0) { + if (epm_grow(&err_buf, &err_cap, err_len + (size_t)n) != 0) { + drain_fail(out_pipe[0], err_pipe[0], pid, out_buf, err_buf); + return -1; + } + + memcpy(err_buf + err_len, tmp, (size_t)n); + err_len += (size_t)n; + } + else if (n == 0 || (n < 0 && errno != EINTR)) { + err_eof = 1; + } + } + } + + close(out_pipe[0]); + close(err_pipe[0]); + + int status = 0; + waitpid(pid, &status, 0); + + if (WIFEXITED(status)) { + *code = WEXITSTATUS(status); + } + else if (WIFSIGNALED(status)) { + *code = 128 + WTERMSIG(status); + } + else { + *code = 1; + } + + *out_s = out_buf; + *out_n = (int32_t)out_len; + *err_s = err_buf; + *err_n = (int32_t)err_len; + return 0; +} diff --git a/epm/c/shim.c b/epm/c/shim.c deleted file mode 100644 index e4149acc..00000000 --- a/epm/c/shim.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Platform bits Echo cannot say: readdir over an opaque handle, stat, mkdir, - * rmdir, WEXITSTATUS, a process runner, and SHA-256. - * - * Nothing here is clever. The Echo side owns policy; this file is the seam. - */ - -#define _POSIX_C_SOURCE 200809L - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* ---- terminal ----------------------------------------------------------- */ - -int32_t epm_isatty(int32_t fd) -{ - return isatty(fd) ? 1 : 0; -} - -int32_t epm_columns(int32_t fd) -{ - if (!isatty(fd)) { - return 0; - } - - struct winsize size; - - if (ioctl(fd, TIOCGWINSZ, &size) == 0 && size.ws_col > 0) { - return (int32_t)size.ws_col; - } - - return 0; -} - -/* ---- directory listing -------------------------------------------------- */ - -DIR *epm_opendir(const char *path) -{ - return opendir(path); -} - -/* 1 = wrote a name, 0 = done, -1 = error. skips "." and ".." */ -int32_t epm_readdir(DIR *dir, char *name, uint64_t cap) -{ - if (dir == NULL || name == NULL || cap == 0) { - return -1; - } - - for (;;) { - errno = 0; - struct dirent *ent = readdir(dir); - - if (ent == NULL) { - return errno == 0 ? 0 : -1; - } - - if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { - continue; - } - - size_t n = strlen(ent->d_name); - if (n + 1 > cap) { - return -1; - } - - memcpy(name, ent->d_name, n + 1); - return 1; - } -} - -void epm_closedir(DIR *dir) -{ - if (dir != NULL) { - closedir(dir); - } -} - -/* ---- stat / mkdir / rmdir ---------------------------------------------- */ - -/* 0 missing, 1 file, 2 directory, -1 error */ -int32_t epm_kind(const char *path) -{ - struct stat st; - - if (stat(path, &st) != 0) { - return errno == ENOENT ? 0 : -1; - } - - return S_ISDIR(st.st_mode) ? 2 : 1; -} - -int32_t epm_mkdir(const char *path) -{ - if (mkdir(path, 0755) == 0) { - return 0; - } - - return errno == EEXIST ? 0 : -1; -} - -int32_t epm_rmdir(const char *path) -{ - return rmdir(path) == 0 ? 0 : -1; -} - -int32_t epm_rename(const char *from, const char *to) -{ - if (from == NULL || to == NULL) { - return -1; - } - - return rename(from, to) == 0 ? 0 : -1; -} - -/* ---- process ------------------------------------------------------------ */ - -typedef struct { - char **v; - int32_t n; - int32_t cap; -} epm_argv; - -void *epm_argv_new(void) -{ - return calloc(1, sizeof(epm_argv)); -} - -int32_t epm_argv_push(void *handle, const char *s) -{ - epm_argv *a = handle; - - if (a == NULL || s == NULL) { - return -1; - } - - if (a->n + 1 >= a->cap) { - int32_t cap = a->cap < 8 ? 8 : a->cap * 2; - char **grown = realloc(a->v, (size_t)cap * sizeof(char *)); - - if (grown == NULL) { - return -1; - } - - a->v = grown; - a->cap = cap; - } - - char *copy = strdup(s); - - if (copy == NULL) { - return -1; - } - - a->v[a->n] = copy; - a->n += 1; - a->v[a->n] = NULL; - return 0; -} - -void epm_argv_free(void *handle) -{ - epm_argv *a = handle; - - if (a == NULL) { - return; - } - - int32_t i; - - for (i = 0; i < a->n; i++) { - free(a->v[i]); - } - - free(a->v); - free(a); -} - -static int grow(char **buf, size_t *cap, size_t need) -{ - if (need <= *cap) { - return 0; - } - - size_t next = *cap < 256 ? 256 : *cap; - - while (next < need) { - next *= 2; - } - - char *grown = realloc(*buf, next); - - if (grown == NULL) { - return -1; - } - - *buf = grown; - *cap = next; - return 0; -} - -static void drain_fail(int out_r, int err_r, pid_t pid, char *out_buf, char *err_buf) -{ - close(out_r); - close(err_r); - free(out_buf); - free(err_buf); - waitpid(pid, NULL, 0); -} - -int32_t epm_run( - void *handle, - char **out_s, int32_t *out_n, - char **err_s, int32_t *err_n, - int32_t *code -) -{ - epm_argv *a = handle; - - if (a == NULL || a->n == 0 || a->v == NULL || a->v[0] == NULL) { - return -1; - } - - int out_pipe[2]; - int err_pipe[2]; - - if (pipe(out_pipe) != 0) { - return -1; - } - - if (pipe(err_pipe) != 0) { - close(out_pipe[0]); - close(out_pipe[1]); - return -1; - } - - pid_t pid = fork(); - - if (pid < 0) { - close(out_pipe[0]); - close(out_pipe[1]); - close(err_pipe[0]); - close(err_pipe[1]); - return -1; - } - - if (pid == 0) { - dup2(out_pipe[1], STDOUT_FILENO); - dup2(err_pipe[1], STDERR_FILENO); - close(out_pipe[0]); - close(out_pipe[1]); - close(err_pipe[0]); - close(err_pipe[1]); - execvp(a->v[0], a->v); - _exit(127); - } - - close(out_pipe[1]); - close(err_pipe[1]); - - size_t out_cap = 256; - size_t err_cap = 256; - size_t out_len = 0; - size_t err_len = 0; - char *out_buf = malloc(out_cap); - char *err_buf = malloc(err_cap); - int out_eof = 0; - int err_eof = 0; - - if (out_buf == NULL || err_buf == NULL) { - drain_fail(out_pipe[0], err_pipe[0], pid, out_buf, err_buf); - return -1; - } - - while (!out_eof || !err_eof) { - struct pollfd fds[2]; - nfds_t nfd = 0; - int out_i = -1; - int err_i = -1; - - if (!out_eof) { - fds[nfd].fd = out_pipe[0]; - fds[nfd].events = POLLIN; - out_i = (int)nfd; - nfd += 1; - } - - if (!err_eof) { - fds[nfd].fd = err_pipe[0]; - fds[nfd].events = POLLIN; - err_i = (int)nfd; - nfd += 1; - } - - int pr = poll(fds, nfd, -1); - - if (pr < 0) { - if (errno == EINTR) { - continue; - } - - drain_fail(out_pipe[0], err_pipe[0], pid, out_buf, err_buf); - return -1; - } - - if (out_i >= 0 && (fds[out_i].revents & (POLLIN | POLLHUP | POLLERR))) { - char tmp[512]; - ssize_t n = read(out_pipe[0], tmp, sizeof(tmp)); - - if (n > 0) { - if (grow(&out_buf, &out_cap, out_len + (size_t)n) != 0) { - drain_fail(out_pipe[0], err_pipe[0], pid, out_buf, err_buf); - return -1; - } - - memcpy(out_buf + out_len, tmp, (size_t)n); - out_len += (size_t)n; - } - else if (n == 0 || (n < 0 && errno != EINTR)) { - out_eof = 1; - } - } - - if (err_i >= 0 && (fds[err_i].revents & (POLLIN | POLLHUP | POLLERR))) { - char tmp[512]; - ssize_t n = read(err_pipe[0], tmp, sizeof(tmp)); - - if (n > 0) { - if (grow(&err_buf, &err_cap, err_len + (size_t)n) != 0) { - drain_fail(out_pipe[0], err_pipe[0], pid, out_buf, err_buf); - return -1; - } - - memcpy(err_buf + err_len, tmp, (size_t)n); - err_len += (size_t)n; - } - else if (n == 0 || (n < 0 && errno != EINTR)) { - err_eof = 1; - } - } - } - - close(out_pipe[0]); - close(err_pipe[0]); - - int status = 0; - waitpid(pid, &status, 0); - - if (WIFEXITED(status)) { - *code = WEXITSTATUS(status); - } - else if (WIFSIGNALED(status)) { - *code = 128 + WTERMSIG(status); - } - else { - *code = 1; - } - - *out_s = out_buf; - *out_n = (int32_t)out_len; - *err_s = err_buf; - *err_n = (int32_t)err_len; - return 0; -} - -void epm_free(void *p) -{ - free(p); -} - -/* ---- SHA-256 ------------------------------------------------------------ */ - -typedef struct { - uint32_t state[8]; - uint64_t bitcount; - uint8_t buffer[64]; - size_t fill; -} epm_sha256_ctx; - -static uint32_t rotr(uint32_t x, uint32_t n) -{ - return (x >> n) | (x << (32 - n)); -} - -static void sha256_transform(epm_sha256_ctx *ctx, const uint8_t *block) -{ - static const uint32_t K[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - }; - - uint32_t w[64]; - uint32_t a, b, c, d, e, f, g, h; - size_t i; - - for (i = 0; i < 16; i++) { - w[i] = ((uint32_t)block[i * 4] << 24) - | ((uint32_t)block[i * 4 + 1] << 16) - | ((uint32_t)block[i * 4 + 2] << 8) - | ((uint32_t)block[i * 4 + 3]); - } - - for (i = 16; i < 64; i++) { - uint32_t s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >> 3); - uint32_t s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >> 10); - w[i] = w[i - 16] + s0 + w[i - 7] + s1; - } - - a = ctx->state[0]; - b = ctx->state[1]; - c = ctx->state[2]; - d = ctx->state[3]; - e = ctx->state[4]; - f = ctx->state[5]; - g = ctx->state[6]; - h = ctx->state[7]; - - for (i = 0; i < 64; i++) { - uint32_t S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25); - uint32_t ch = (e & f) ^ ((~e) & g); - uint32_t t1 = h + S1 + ch + K[i] + w[i]; - uint32_t S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); - uint32_t maj = (a & b) ^ (a & c) ^ (b & c); - uint32_t t2 = S0 + maj; - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; -} - -void *epm_sha256_new(void) -{ - epm_sha256_ctx *ctx = malloc(sizeof(*ctx)); - if (ctx == NULL) { - return NULL; - } - - ctx->state[0] = 0x6a09e667; - ctx->state[1] = 0xbb67ae85; - ctx->state[2] = 0x3c6ef372; - ctx->state[3] = 0xa54ff53a; - ctx->state[4] = 0x510e527f; - ctx->state[5] = 0x9b05688c; - ctx->state[6] = 0x1f83d9ab; - ctx->state[7] = 0x5be0cd19; - ctx->bitcount = 0; - ctx->fill = 0; - return ctx; -} - -void epm_sha256_update(void *handle, const uint8_t *data, uint64_t n) -{ - epm_sha256_ctx *ctx = handle; - size_t i = 0; - - ctx->bitcount += n * 8; - - while (i < n) { - size_t room = 64 - ctx->fill; - size_t take = (n - i) < room ? (size_t)(n - i) : room; - memcpy(ctx->buffer + ctx->fill, data + i, take); - ctx->fill += take; - i += take; - - if (ctx->fill == 64) { - sha256_transform(ctx, ctx->buffer); - ctx->fill = 0; - } - } -} - -void epm_sha256_final(void *handle, uint8_t *out) -{ - epm_sha256_ctx *ctx = handle; - uint64_t bits = ctx->bitcount; - size_t i; - - ctx->buffer[ctx->fill++] = 0x80; - - if (ctx->fill > 56) { - while (ctx->fill < 64) { - ctx->buffer[ctx->fill++] = 0; - } - sha256_transform(ctx, ctx->buffer); - ctx->fill = 0; - } - - while (ctx->fill < 56) { - ctx->buffer[ctx->fill++] = 0; - } - - for (i = 0; i < 8; i++) { - ctx->buffer[63 - i] = (uint8_t)(bits >> (i * 8)); - } - sha256_transform(ctx, ctx->buffer); - - for (i = 0; i < 8; i++) { - out[i * 4] = (uint8_t)(ctx->state[i] >> 24); - out[i * 4 + 1] = (uint8_t)(ctx->state[i] >> 16); - out[i * 4 + 2] = (uint8_t)(ctx->state[i] >> 8); - out[i * 4 + 3] = (uint8_t)ctx->state[i]; - } - - free(ctx); -} diff --git a/epm/c/win32.c b/epm/c/win32.c new file mode 100644 index 00000000..cda6b3f5 --- /dev/null +++ b/epm/c/win32.c @@ -0,0 +1,727 @@ +/* + * Windows half of the epm shim: directory, stat, mkdir, a process runner. + */ + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include + +#include "internal.h" + +#include +#include +#include +#include +#include + +int32_t epm_isatty(int32_t fd) +{ + return _isatty(fd) ? 1 : 0; +} + +int32_t epm_columns(int32_t fd) +{ + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + + if (!_isatty(fd)) { + return 0; + } + + handle = (HANDLE)_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + return 0; + } + + if (GetConsoleScreenBufferInfo(handle, &info) && info.dwSize.X > 0) { + return (int32_t)info.dwSize.X; + } + + return 0; +} + +static wchar_t *utf8_to_wide(const char *utf8) +{ + int needed; + wchar_t *wide; + + if (utf8 == NULL) { + return NULL; + } + + needed = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); + if (needed <= 0) { + return NULL; + } + + wide = malloc((size_t)needed * sizeof(wchar_t)); + if (wide == NULL) { + return NULL; + } + + if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wide, needed) <= 0) { + free(wide); + return NULL; + } + + return wide; +} + +static char *wide_to_utf8(const wchar_t *wide) +{ + int needed; + char *utf8; + + if (wide == NULL) { + return NULL; + } + + needed = WideCharToMultiByte(CP_UTF8, 0, wide, -1, NULL, 0, NULL, NULL); + if (needed <= 0) { + return NULL; + } + + utf8 = malloc((size_t)needed); + if (utf8 == NULL) { + return NULL; + } + + if (WideCharToMultiByte(CP_UTF8, 0, wide, -1, utf8, needed, NULL, NULL) <= 0) { + free(utf8); + return NULL; + } + + return utf8; +} + +static void slash_to_backslash(wchar_t *path) +{ + wchar_t *p; + + for (p = path; *p != L'\0'; p++) { + if (*p == L'/') { + *p = L'\\'; + } + } +} + +static int is_unc(const wchar_t *path) +{ + return path[0] == L'\\' && path[1] == L'\\'; +} + +/* absolute Win32 path with a \\?\ prefix so vendor trees past MAX_PATH work */ +static wchar_t *wide_ntpath(const char *utf8) +{ + wchar_t *wide; + wchar_t *full; + DWORD needed; + DWORD n; + wchar_t *out; + + wide = utf8_to_wide(utf8); + if (wide == NULL) { + return NULL; + } + + slash_to_backslash(wide); + + if (wcsncmp(wide, L"\\\\?\\", 4) == 0) { + return wide; + } + + needed = GetFullPathNameW(wide, 0, NULL, NULL); + if (needed == 0) { + free(wide); + return NULL; + } + + full = malloc((size_t)needed * sizeof(wchar_t)); + if (full == NULL) { + free(wide); + return NULL; + } + + n = GetFullPathNameW(wide, needed, full, NULL); + free(wide); + if (n == 0 || n >= needed) { + free(full); + return NULL; + } + + if (is_unc(full)) { + /* \\server\share -> \\?\UNC\server\share */ + size_t n = wcslen(full); + out = malloc((8 + n + 1) * sizeof(wchar_t)); + if (out == NULL) { + free(full); + return NULL; + } + memcpy(out, L"\\\\?\\UNC", 7 * sizeof(wchar_t)); + memcpy(out + 7, full + 1, n * sizeof(wchar_t)); + free(full); + return out; + } + + { + size_t n = wcslen(full); + out = malloc((4 + n + 1) * sizeof(wchar_t)); + if (out == NULL) { + free(full); + return NULL; + } + memcpy(out, L"\\\\?\\", 4 * sizeof(wchar_t)); + memcpy(out + 4, full, (n + 1) * sizeof(wchar_t)); + free(full); + return out; + } +} + +typedef struct { + HANDLE handle; + WIN32_FIND_DATAW data; + int first; + int done; +} epm_dir; + +void *epm_opendir(const char *path) +{ + epm_dir *dir; + wchar_t *wide; + wchar_t *pattern; + size_t n; + + if (path == NULL || path[0] == '\0') { + return NULL; + } + + wide = wide_ntpath(path); + if (wide == NULL) { + return NULL; + } + + n = wcslen(wide); + pattern = malloc((n + 3) * sizeof(wchar_t)); + if (pattern == NULL) { + free(wide); + return NULL; + } + + memcpy(pattern, wide, (n + 1) * sizeof(wchar_t)); + if (pattern[n - 1] != L'\\') { + pattern[n] = L'\\'; + n += 1; + } + pattern[n] = L'*'; + pattern[n + 1] = L'\0'; + free(wide); + + dir = calloc(1, sizeof(*dir)); + if (dir == NULL) { + free(pattern); + return NULL; + } + + dir->handle = FindFirstFileW(pattern, &dir->data); + free(pattern); + if (dir->handle == INVALID_HANDLE_VALUE) { + free(dir); + return NULL; + } + + dir->first = 1; + return dir; +} + +int32_t epm_readdir(void *handle, char *name, uint64_t cap) +{ + epm_dir *dir = handle; + char *utf8; + size_t n; + + if (dir == NULL || name == NULL || cap == 0) { + return -1; + } + + for (;;) { + if (dir->done) { + return 0; + } + + if (!dir->first) { + if (!FindNextFileW(dir->handle, &dir->data)) { + dir->done = 1; + return 0; + } + } + dir->first = 0; + + if (wcscmp(dir->data.cFileName, L".") == 0 + || wcscmp(dir->data.cFileName, L"..") == 0) { + continue; + } + + utf8 = wide_to_utf8(dir->data.cFileName); + if (utf8 == NULL) { + return -1; + } + + n = strlen(utf8); + if (n + 1 > cap) { + free(utf8); + return -1; + } + + memcpy(name, utf8, n + 1); + free(utf8); + return 1; + } +} + +void epm_closedir(void *handle) +{ + epm_dir *dir = handle; + + if (dir == NULL) { + return; + } + + if (dir->handle != INVALID_HANDLE_VALUE && dir->handle != NULL) { + FindClose(dir->handle); + } + + free(dir); +} + +int32_t epm_kind(const char *path) +{ + wchar_t *wide; + DWORD attributes; + + if (path == NULL) { + return -1; + } + + wide = wide_ntpath(path); + if (wide == NULL) { + return -1; + } + + attributes = GetFileAttributesW(wide); + free(wide); + + if (attributes == INVALID_FILE_ATTRIBUTES) { + DWORD err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { + return 0; + } + return -1; + } + + return (attributes & FILE_ATTRIBUTE_DIRECTORY) ? 2 : 1; +} + +int32_t epm_mkdir(const char *path) +{ + wchar_t *wide; + BOOL ok; + + if (path == NULL) { + return -1; + } + + wide = wide_ntpath(path); + if (wide == NULL) { + return -1; + } + + ok = CreateDirectoryW(wide, NULL); + free(wide); + + if (ok) { + return 0; + } + + return GetLastError() == ERROR_ALREADY_EXISTS ? 0 : -1; +} + +int32_t epm_rmdir(const char *path) +{ + wchar_t *wide; + BOOL ok; + + if (path == NULL) { + return -1; + } + + wide = wide_ntpath(path); + if (wide == NULL) { + return -1; + } + + ok = RemoveDirectoryW(wide); + free(wide); + return ok ? 0 : -1; +} + +int32_t epm_rename(const char *from, const char *to) +{ + wchar_t *wfrom; + wchar_t *wto; + BOOL ok; + + if (from == NULL || to == NULL) { + return -1; + } + + wfrom = wide_ntpath(from); + wto = wide_ntpath(to); + if (wfrom == NULL || wto == NULL) { + free(wfrom); + free(wto); + return -1; + } + + ok = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING); + free(wfrom); + free(wto); + return ok ? 0 : -1; +} + +static int quote_windows_arg(const char *arg, char **out) +{ + size_t i; + size_t n; + int need_quote = 0; + char *dst; + size_t o = 0; + + if (arg == NULL) { + return -1; + } + + n = strlen(arg); + if (n == 0) { + need_quote = 1; + } + + for (i = 0; i < n; i++) { + if (arg[i] == ' ' || arg[i] == '\t' || arg[i] == '"') { + need_quote = 1; + } + } + + dst = malloc(n * 2 + 4); + if (dst == NULL) { + return -1; + } + + if (!need_quote) { + memcpy(dst, arg, n + 1); + *out = dst; + return 0; + } + + dst[o++] = '"'; + for (i = 0; i < n; i++) { + size_t slashes = 0; + while (i < n && arg[i] == '\\') { + slashes += 1; + i += 1; + } + if (i == n) { + while (slashes--) { + dst[o++] = '\\'; + dst[o++] = '\\'; + } + break; + } + if (arg[i] == '"') { + while (slashes--) { + dst[o++] = '\\'; + dst[o++] = '\\'; + } + dst[o++] = '\\'; + dst[o++] = '"'; + } + else { + while (slashes--) { + dst[o++] = '\\'; + } + dst[o++] = arg[i]; + } + } + dst[o++] = '"'; + dst[o] = '\0'; + *out = dst; + return 0; +} + +static int is_cmd_exe(const char *name) +{ + const char *slash; + const char *base; + + if (name == NULL) { + return 0; + } + + slash = strrchr(name, '\\'); + if (slash == NULL) { + slash = strrchr(name, '/'); + } + base = slash == NULL ? name : slash + 1; + + return _stricmp(base, "cmd") == 0 || _stricmp(base, "cmd.exe") == 0; +} + +static wchar_t *windows_command_line(const epm_argv *a) +{ + char *line = NULL; + size_t cap = 0; + size_t len = 0; + int32_t i; + wchar_t *wide; + + /* cmd's /c remainder is a command string, not an argv word. quoting it + * the CommandLineToArgvW way is the encoding cmd then misreads. same + * exception Compiler::quote_windows_arg's tests pin. */ + if (a->n >= 3 && strcmp(a->v[1], "/c") == 0 && is_cmd_exe(a->v[0])) { + char *quoted = NULL; + size_t qn; + size_t pn; + + if (quote_windows_arg(a->v[0], "ed) != 0) { + return NULL; + } + + qn = strlen(quoted); + pn = strlen(a->v[2]); + line = malloc(qn + pn + 6); + if (line == NULL) { + free(quoted); + return NULL; + } + memcpy(line, quoted, qn); + memcpy(line + qn, " /c ", 4); + memcpy(line + qn + 4, a->v[2], pn + 1); + free(quoted); + wide = utf8_to_wide(line); + free(line); + return wide; + } + + for (i = 0; i < a->n; i++) { + char *quoted = NULL; + size_t qn; + + if (quote_windows_arg(a->v[i], "ed) != 0) { + free(line); + return NULL; + } + + qn = strlen(quoted); + if (epm_grow(&line, &cap, len + qn + 2) != 0) { + free(quoted); + free(line); + return NULL; + } + + if (len > 0) { + line[len++] = ' '; + } + memcpy(line + len, quoted, qn); + len += qn; + line[len] = '\0'; + free(quoted); + } + + if (line == NULL) { + return NULL; + } + + wide = utf8_to_wide(line); + free(line); + return wide; +} + +static int drain_pipe(HANDLE pipe, char **buf, size_t *cap, size_t *len) +{ + char tmp[4096]; + DWORD got = 0; + + for (;;) { + if (!ReadFile(pipe, tmp, sizeof(tmp), &got, NULL)) { + return GetLastError() == ERROR_BROKEN_PIPE ? 0 : -1; + } + + if (got == 0) { + return 0; + } + + if (epm_grow(buf, cap, *len + (size_t)got + 1) != 0) { + return -1; + } + + memcpy(*buf + *len, tmp, (size_t)got); + *len += (size_t)got; + } +} + +static int read_available(HANDLE pipe, char **buf, size_t *cap, size_t *len) +{ + DWORD avail = 0; + DWORD got = 0; + + if (!PeekNamedPipe(pipe, NULL, 0, NULL, &avail, NULL)) { + return GetLastError() == ERROR_BROKEN_PIPE ? 0 : -1; + } + + if (avail == 0) { + return 1; + } + + if (epm_grow(buf, cap, *len + (size_t)avail + 1) != 0) { + return -1; + } + + if (!ReadFile(pipe, *buf + *len, avail, &got, NULL)) { + return GetLastError() == ERROR_BROKEN_PIPE ? 0 : -1; + } + + *len += (size_t)got; + return 1; +} + +static void close_pipes(HANDLE out_r, HANDLE out_w, HANDLE err_r, HANDLE err_w) +{ + if (out_r) CloseHandle(out_r); + if (out_w) CloseHandle(out_w); + if (err_r) CloseHandle(err_r); + if (err_w) CloseHandle(err_w); +} + +int32_t epm_run( + void *handle, + char **out_s, int32_t *out_n, + char **err_s, int32_t *err_n, + int32_t *code +) +{ + epm_argv *a = handle; + SECURITY_ATTRIBUTES sa; + HANDLE out_r = NULL; + HANDLE out_w = NULL; + HANDLE err_r = NULL; + HANDLE err_w = NULL; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + wchar_t *cmdline; + wchar_t *app = NULL; + char *out_buf = NULL; + char *err_buf = NULL; + size_t out_cap = 256; + size_t err_cap = 256; + size_t out_len = 0; + size_t err_len = 0; + DWORD exit_code = 1; + + if (a == NULL || a->n == 0 || a->v == NULL || a->v[0] == NULL) { + return -1; + } + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + if (!CreatePipe(&out_r, &out_w, &sa, 0) || !CreatePipe(&err_r, &err_w, &sa, 0)) { + close_pipes(out_r, out_w, err_r, err_w); + return -1; + } + + SetHandleInformation(out_r, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(err_r, HANDLE_FLAG_INHERIT, 0); + + cmdline = windows_command_line(a); + if (cmdline == NULL) { + close_pipes(out_r, out_w, err_r, err_w); + return -1; + } + + if (strchr(a->v[0], '/') != NULL || strchr(a->v[0], '\\') != NULL + || (a->v[0][0] != '\0' && a->v[0][1] == ':')) { + app = wide_ntpath(a->v[0]); + } + + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = out_w; + si.hStdError = err_w; + + if (!CreateProcessW( + app, cmdline, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { + free(app); + free(cmdline); + close_pipes(out_r, out_w, err_r, err_w); + return -1; + } + + free(app); + free(cmdline); + CloseHandle(out_w); + CloseHandle(err_w); + CloseHandle(pi.hThread); + + out_buf = malloc(out_cap); + err_buf = malloc(err_cap); + if (out_buf == NULL || err_buf == NULL) { + free(out_buf); + free(err_buf); + CloseHandle(out_r); + CloseHandle(err_r); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + return -1; + } + + for (;;) { + DWORD wait = WaitForSingleObject(pi.hProcess, 15); + if (read_available(out_r, &out_buf, &out_cap, &out_len) < 0 + || read_available(err_r, &err_buf, &err_cap, &err_len) < 0) { + free(out_buf); + free(err_buf); + CloseHandle(out_r); + CloseHandle(err_r); + CloseHandle(pi.hProcess); + return -1; + } + if (wait == WAIT_OBJECT_0) { + if (drain_pipe(out_r, &out_buf, &out_cap, &out_len) < 0 + || drain_pipe(err_r, &err_buf, &err_cap, &err_len) < 0) { + free(out_buf); + free(err_buf); + CloseHandle(out_r); + CloseHandle(err_r); + CloseHandle(pi.hProcess); + return -1; + } + break; + } + } + + GetExitCodeProcess(pi.hProcess, &exit_code); + CloseHandle(pi.hProcess); + CloseHandle(out_r); + CloseHandle(err_r); + + *code = (int32_t)exit_code; + *out_s = out_buf; + *out_n = (int32_t)out_len; + *err_s = err_buf; + *err_n = (int32_t)err_len; + return 0; +} diff --git a/epm/module.eco b/epm/module.eco index 97620363..552a2cde 100644 --- a/epm/module.eco +++ b/epm/module.eco @@ -14,7 +14,12 @@ #[epm::description: "a package manager for Echo"] #[sources: "src/**/*.eco"] -#[cc: sources "c/shim.c"] +#[cc: sources "c/common.c"] +#[if: os == windows] +#[cc: sources "c/win32.c"] +#[else] +#[cc: sources "c/posix.c"] +#[end] #[depends: "../../echolibs/libjson"] #[depends: "../../echolibs/libcurl"] @@ -23,16 +28,27 @@ // the archive still needs zlib and a TLS stack; those names are not on // libcurl's dynamic manifest, so they do not collide. #[if: static_curl] -#[link: lib "z"] #[if: os == linux] +#[link: lib "z"] #[link: lib "dl"] #[end] #[if: os == darwin] +#[link: lib "z"] #[link: framework "Security"] #[link: framework "CoreFoundation"] #[link: framework "CoreServices"] #[link: framework "SystemConfiguration"] #[end] +#[if: os == windows] +#[link: lib "ws2_32"] +#[link: lib "crypt32"] +#[link: lib "advapi32"] +#[link: lib "bcrypt"] +#[link: lib "normaliz"] +#[link: lib "wldap32"] +#[link: lib "secur32"] +#[link: lib "iphlpapi"] +#[end] #[end] #[target: exe { name: "epm", entry: "src/main.eco" }] diff --git a/epm/src/manifest/read.eco b/epm/src/manifest/read.eco index 75547e75..75f21a8d 100644 --- a/epm/src/manifest/read.eco +++ b/epm/src/manifest/read.eco @@ -100,7 +100,7 @@ public function find_module(const string& $start) : string? * Which echoc this process should shell out to. * * A non-empty $from_env is ECHOC and wins. Otherwise the echoc sitting - * next to this binary, which is where install.sh puts it. A bare + * next to this binary, which is where the installer puts it. A bare * `echoc` is PATH. */ public function echoc_from(const string& $exe_dir, const string& $from_env) : string @@ -109,13 +109,13 @@ public function echoc_from(const string& $exe_dir, const string& $from_env) : st return $from_env; } - string $sibling = sys::join($exe_dir, 'echoc'); + string $sibling = sys::join($exe_dir, sys::host_exe('echoc')); if (sys::is_file($sibling)) { return $sibling; } - return 'echoc'; + return sys::host_exe('echoc'); } public function echoc_bin() : string diff --git a/epm/src/sys/bindings.eco b/epm/src/sys/bindings.eco index 88ccde3f..9a06673b 100644 --- a/epm/src/sys/bindings.eco +++ b/epm/src/sys/bindings.eco @@ -1,6 +1,7 @@ /* - * The C symbols in c/shim.c. One block, declared once - a C symbol may only - * appear once per module. Everything else in epm::sys calls these. + * The C symbols in c/{common,posix,win32}.c. One block, declared once - a C + * symbol may only appear once per module. Everything else in epm::sys calls + * these. */ namespace epm::sys; diff --git a/epm/src/sys/path.eco b/epm/src/sys/path.eco index a05fd6aa..66b9da8f 100644 --- a/epm/src/sys/path.eco +++ b/epm/src/sys/path.eco @@ -1,9 +1,58 @@ /** - * Path joining and splitting. `/` only - epm is POSIX. + * Path joining and splitting. `/` on POSIX; `/` and `\` on Windows, plus + * drive prefixes (`C:`). Forward slashes still work on Windows. */ namespace epm::sys; +function is_sep(uint8 $b) : bool +{ +#[if: os == windows] + return $b == 47 || $b == 92; +#[else] + return $b == 47; +#[end] +} + +function last_sep(const string& $path) : usize +{ + usize $last = $path->size(); + usize $i = 0; + + while ($i < $path->size()) { + if (is_sep($path->byte($i))) { + $last = $i; + } + + $i = $i + 1; + } + + return $last; +} + +public function is_absolute(const string& $path) : bool +{ + if ($path->empty()) { + return false; + } + + if (is_sep($path->byte(0))) { + return true; + } + +#[if: os == windows] + if ($path->size() >= 3) { + uint8 $drive = $path->byte(0); + bool $letter = ($drive >= 65 && $drive <= 90) || ($drive >= 97 && $drive <= 122); + if ($letter && $path->byte(1) == 58 && is_sep($path->byte(2))) { + return true; + } + } +#[end] + + return false; +} + public function join(const string& $a, const string& $b) : string { if ($a->empty()) { @@ -14,31 +63,45 @@ public function join(const string& $a, const string& $b) : string return $a; } - if ($b->starts('/')) { + if (is_absolute($b)) { return $b; } - if ($a->ends('/')) { + if (is_sep($a->byte($a->size() - 1))) { string $glued = "{$a}{$b}"; return $glued->clone(); } +#[if: os == windows] + // keep the separator the left side already uses, so `/tmp` + `a` stays + // `/tmp/a` in tests and `C:\echo` + `bin` becomes `C:\echo\bin` + usize $i = 0; + while ($i < $a->size()) { + if ($a->byte($i) == 92) { + string $joined = "{$a}\\{$b}"; + return $joined->clone(); + } + $i = $i + 1; + } +#[end] + string $joined = "{$a}/{$b}"; return $joined->clone(); } -public function basename(const string& $path) : string +public function host_exe(const string& $name) : string { - usize $last = $path->size(); - usize $i = 0; - - while ($i < $path->size()) { - if ($path->byte($i) == 47) { - $last = $i; - } +#[if: os == windows] + string $exe = "{$name}.exe"; + return $exe->clone(); +#[else] + return $name; +#[end] +} - $i = $i + 1; - } +public function basename(const string& $path) : string +{ + usize $last = last_sep($path); if ($last == $path->size()) { return $path; @@ -50,34 +113,25 @@ public function basename(const string& $path) : string public function dirname(const string& $path) : string { - usize $last = $path->size(); - usize $i = 0; - - while ($i < $path->size()) { - if ($path->byte($i) == 47) { - $last = $i; - } - - $i = $i + 1; - } + usize $last = last_sep($path); if ($last == $path->size()) { return '.'; } if ($last == 0) { - return '/'; + string $root = $path->sub(0, 1); + return $root->clone(); } - string $dir = $path->sub(0, $last); - return $dir->clone(); -} - -public function is_absolute(const string& $path) : bool -{ - if ($path->empty()) { - return false; +#[if: os == windows] + // C:\foo -> C:\, not C: + if ($last >= 2 && $path->byte($last - 1) == 58 && is_sep($path->byte($last))) { + string $drive = $path->sub(0, $last + 1); + return $drive->clone(); } +#[end] - return $path->starts('/'); + string $dir = $path->sub(0, $last); + return $dir->clone(); } diff --git a/epm/tests/path.eco b/epm/tests/path.eco index 65b98f62..11575888 100644 --- a/epm/tests/path.eco +++ b/epm/tests/path.eco @@ -54,6 +54,28 @@ test is_absolute_is_a_leading_slash assert(!sys::is_absolute('')); } +#[if: os == windows] +test windows_paths_accept_a_drive_and_a_backslash +{ + assert(sys::is_absolute('C:/echo')); + assert(sys::is_absolute('C:\\echo')); + assert(!sys::is_absolute('echo')); + assert(sys::basename('C:\\echo\\bin\\echoc.exe') == 'echoc.exe'); + assert(sys::dirname('C:\\echo\\bin\\echoc.exe') == 'C:\\echo\\bin'); + assert(sys::dirname('C:\\echo') == 'C:\\'); + assert(sys::join('C:\\echo', 'bin') == 'C:\\echo\\bin'); +} + +test host_exe_is_the_platform_binary_name +{ +#[if: os == windows] + assert(sys::host_exe('echoc') == 'echoc.exe'); +#[else] + assert(sys::host_exe('echoc') == 'echoc'); +#[end] +} +#[end] + test a_package_name_may_have_a_vendor_prefix { assert(manifest::usable_name('libcurl')); diff --git a/epm/tests/proc.eco b/epm/tests/proc.eco index d4c49602..18d576d5 100644 --- a/epm/tests/proc.eco +++ b/epm/tests/proc.eco @@ -1,5 +1,6 @@ use epm::sys; +#[if: os != windows] test run_captures_stdout { array $a = array(); @@ -40,3 +41,39 @@ test run_passes_an_argument_that_would_break_a_shell assert($ran->ok()); assert($ran->out == "it's"); } +#[end] + +#[if: os == windows] +test run_captures_stdout +{ + array $a = array(); + $a[] = 'cmd'; + $a[] = '/c'; + $a[] = 'echo hello'; + sys::Run $ran = sys::run($a); + assert($ran->ok()); + assert($ran->out->contains('hello')); +} + +test run_captures_a_nonzero_exit +{ + array $a = array(); + $a[] = 'cmd'; + $a[] = '/c'; + $a[] = 'exit 7'; + sys::Run $ran = sys::run($a); + assert(!$ran->ok()); + assert($ran->code == 7); +} + +test run_passes_an_argument_that_would_break_a_shell +{ + array $a = array(); + $a[] = 'cmd'; + $a[] = '/c'; + $a[] = "echo it's"; + sys::Run $ran = sys::run($a); + assert($ran->ok()); + assert($ran->out->contains("it's")); +} +#[end] diff --git a/epm/tests/scratch.eco b/epm/tests/scratch.eco index 3fa6d097..c0864713 100644 --- a/epm/tests/scratch.eco +++ b/epm/tests/scratch.eco @@ -10,7 +10,8 @@ use epm::util; public function dir(const string& $name) : string { - string $path = sys::join('/tmp', "epm-t-{$name}"); + string $tmp = string::from(std::env::tmp()); + string $path = sys::join($tmp, "epm-t-{$name}"); sys::remove_all($path); assert(sys::mkdir_p($path)); return $path; diff --git a/examples/analog_clock/.gitignore b/examples/analog_clock/.gitignore index 43a0bea7..bb24a6d7 100644 --- a/examples/analog_clock/.gitignore +++ b/examples/analog_clock/.gitignore @@ -1,3 +1,4 @@ # what `echoc build -o clock` leaves here: the binary, and the build directory holding everything else clock +clock.exe ecobuild/ diff --git a/examples/analog_clock/README.md b/examples/analog_clock/README.md index 35e14f21..09e7ebd5 100644 --- a/examples/analog_clock/README.md +++ b/examples/analog_clock/README.md @@ -49,8 +49,9 @@ Or as a native binary: ./clock ``` -Ctrl-C quits. Nothing about the terminal is switched on that would need switching back off, so the -shell you return to is the one you left. +Ctrl-C quits. Nothing about the terminal is switched on here that would need switching back off, so +the shell you return to is the one you left. The pictures go through `std::io::print` — `stdout`, +not `echo` — so a Windows console gets Unicode the same way a Unix terminal does. Give it an `HH:MM:SS` and it draws that one frame and exits instead — which is how the picture above was made, and the quickest way to see whether a change to the drawing did what you meant: @@ -93,7 +94,7 @@ with the bytes is `two_digits_at`, and that is tested. | | | |---|---| | [module.eco](module.eco) | the manifest: a name, a version, `src/*.eco`, `tests/*.eco`, and the one program | -| [src/clock.eco](src/clock.eco) | `extern` bindings for `time`, `localtime` and `usleep`; `Time`; reading one off the command line; where each hand points | +| [src/clock.eco](src/clock.eco) | `extern` bindings for `time`, `localtime` and `usleep` (`Sleep` on Windows); `Time`; reading one off the command line; where each hand points | | [src/canvas.eco](src/canvas.eco) | the character grid, and turning it into one printable frame | | [src/face.eco](src/face.eco) | `Dial`, the `Renderable` interface, and the three layers that conform to it — the dial, the hands, the `HH:MM:SS` readout | | [src/renderer.eco](src/renderer.eco) | `Renderer` — a canvas, a list of layers, and `frame` — one whole picture | diff --git a/examples/analog_clock/src/canvas.eco b/examples/analog_clock/src/canvas.eco index 3e15e8e6..c75e2c75 100644 --- a/examples/analog_clock/src/canvas.eco +++ b/examples/analog_clock/src/canvas.eco @@ -125,9 +125,8 @@ struct Canvas } } - // the whole picture as one string. `echo` prints one expression and always adds a newline, so a - // frame has to arrive in a single statement - printing cell by cell would flicker and cost a write - // syscall per glyph. + // the whole picture as one string. `print` takes one `string`, so a frame has to arrive in a + // single statement - printing cell by cell would flicker and cost a write syscall per glyph. // // nothing terminal-specific in here, deliberately: homing the cursor is what the *animation* does // between frames, and a canvas that knew about it could not hand back a picture anybody could diff --git a/examples/analog_clock/src/clock.eco b/examples/analog_clock/src/clock.eco index d11321f1..ccffe910 100644 --- a/examples/analog_clock/src/clock.eco +++ b/examples/analog_clock/src/clock.eco @@ -2,7 +2,8 @@ * Where the time comes from, and where the program waits. * * The stdlib has neither, so both are libc through `extern`. That is the intended shape: a binding is - * a declaration, not a wrapper the compiler has to know about. + * a declaration, not a wrapper the compiler has to know about. Windows has no `usleep`; the wait is + * kernel32 `Sleep`, in milliseconds. */ namespace clock; @@ -30,8 +31,17 @@ struct tm extern { function time as c_time(ptr $out) : int64; function localtime as c_localtime(ptr $t) : ptr; +} + +#[if: os == windows] +extern { + function Sleep as c_Sleep(uint32 $ms) : void; +} +#[else] +extern { function usleep as c_usleep(uint32 $usec) : int32; } +#[end] // the time of day, and nothing else - the rest of the program never sees a `tm` struct Time @@ -55,7 +65,11 @@ function now() : Time function sleep_ms(uint32 $ms) : void { + #[if: os == windows] + c_Sleep($ms); + #[else] c_usleep($ms * 1000); + #[end] } // -- reading a time off the command line --------------------------------------------------------- diff --git a/examples/analog_clock/src/main.eco b/examples/analog_clock/src/main.eco index 6da1c21f..06148eb0 100644 --- a/examples/analog_clock/src/main.eco +++ b/examples/analog_clock/src/main.eco @@ -9,10 +9,12 @@ * animates until Ctrl-C. The first is how the picture in the README was made, and the quickest way to * see what a change to the drawing did; the second is the clock. * - * Ctrl-C is the way out of the live one, and it cannot be trapped - so nothing about the terminal is - * switched on that would have to be switched back off. No alternate screen buffer, no hidden cursor: - * the screen is cleared once, and every frame after that only homes the cursor and overwrites what is - * there. + * Pictures go through `std::io::print` rather than `echo`. `echo` of a string is a raw `_write`, + * which a Windows console decodes as OEM; `print` is `stdout->write`, and that is the path the + * standard library makes Unicode on every host. Ctrl-C cannot be trapped, so nothing about the + * terminal is switched on here that would have to be switched back off - no alternate screen, no + * hidden cursor. The screen is cleared once, and every frame after that only homes the cursor and + * overwrites what is there. */ // a dial eleven rows in radius, in cells twice as tall as they are wide @@ -32,10 +34,10 @@ clock::Time $fixed = clock::Time(0, 0, 0); if (clock::time_from_args(&$fixed)) { // no screen clear and no cursor-home dance: one picture, printed where the cursor already is - echo $renderer->frame(&$fixed); + std::io::print($renderer->frame(&$fixed)); } else { - echo "\x1b[2J"; + std::io::print("\x1b[2J"); while (true) { clock::Time $now = clock::now(); @@ -44,7 +46,7 @@ else { // animation, and a `frame` that emitted it could not hand back one anybody could compare string $frame = "\x1b[H"; $frame->append($renderer->frame(&$now)); - echo $frame; + std::io::print($frame); // five redraws a second: the second hand lands on its tick promptly without the process spinning clock::sleep_ms(200); diff --git a/include/AST/ASTModuleEmbedder.h b/include/AST/ASTModuleEmbedder.h index d6e0f3d9..19644d90 100644 --- a/include/AST/ASTModuleEmbedder.h +++ b/include/AST/ASTModuleEmbedder.h @@ -5,8 +5,17 @@ #include "AST/ASTModule.h" +#include +#include + namespace AST { + // the path written into the embedded header for this file. a source under + // STDLIB_SOURCE_DIR becomes `stdlib:`, so a Windows native + // path cannot appear as a C string - `\ordered_map` is `\o{...}` and `\utf8` + // is `\uXXXX`, both compile errors rather than a path + std::string embedded_source_path(const std::filesystem::path &path); + void write_embedded_module(AST::Module &module, const std::string &output_path); }; #endif diff --git a/include/Compiler/BuildLayout.h b/include/Compiler/BuildLayout.h index 0663ec8d..7be0ea46 100644 --- a/include/Compiler/BuildLayout.h +++ b/include/Compiler/BuildLayout.h @@ -134,6 +134,12 @@ namespace Compiler return _scratch; } + // a loose .eco file has no project directory. the fallback used to live under the + // system temp path; a linked runner there is what Windows Defender flags as a dropper + bool scratch_is_temporary() const { + return _scratch_is_temporary; + } + // where a C object goes when its module's own store could not be written. The `cc` join lives // here beside module_cc_dir's: a caller spelling it is the fourth answer this class replaced std::filesystem::path scratch_cc_dir() const; diff --git a/include/Compiler/CBuild.h b/include/Compiler/CBuild.h index 3041391a..2286133d 100644 --- a/include/Compiler/CBuild.h +++ b/include/Compiler/CBuild.h @@ -158,6 +158,10 @@ namespace Compiler std::filesystem::path &out_library, std::string &out_error ); + + // defined COFF text/data names from `llvm-nm --extern-only --defined-only` output. + // isolated from the linker so the scrape can be tested without running clang + std::vector coff_exports_from_nm(const std::string &nm_output); }; #endif diff --git a/include/Compiler/HostTool.h b/include/Compiler/HostTool.h index e94dba89..37210b18 100644 --- a/include/Compiler/HostTool.h +++ b/include/Compiler/HostTool.h @@ -3,7 +3,9 @@ #pragma once +#include #include +#include #include namespace Compiler @@ -18,6 +20,82 @@ namespace Compiler // clang diagnostic reaches the terminal as clang wrote it rather than through a renderer that would // have to pretend it understood it bool run_tool(const std::vector &argv); + + // the same spawn as run_tool, returning the child's exit code. 127 if the program was not found + // or would not start. `echoc run` on a host that cannot JIT uses this so the program's status is + // the process's status + int run_wait(const std::vector &argv); + + // execute `program` with `argv` as the child's argv. argv[0] is the name the child + // sees, which is not always the path that was exec'd - a JIT-compatible `echoc run` + // keeps the source file as argv[0] while spawning the scratch binary + int run_wait(const std::string &program, const std::vector &argv); + +#if defined(_WIN32) + // CommandLineToArgvW encoding of one argv word: 2n / 2n+1 backslashes before a quote, empty + // arguments as `""`. CreateProcess is the consumer; tests pin the encoding + std::string quote_windows_arg(const std::string &arg); +#endif + + // the clang this echoc was built with, so a GNU-ABI `clang` sitting first + // on PATH (llvm-mingw) cannot link the MSVC objects we emit. empty if none + std::string host_clang(); + + // directory containing this process's executable. A released Windows + // echoc looks here (and one level up) for the bundled clang, lld-link + // and sysroot; a local `build/echoc` has none of those and falls through + std::filesystem::path process_directory(); + + // `{prefix}/sysroot` next to a released Windows echoc: `lib/` is the + // MSVC/UCRT import libraries, `include/` is the headers clang needs + // for `#[cc:]`. empty on Unix and on a local Windows build that has + // not been bundled + std::filesystem::path windows_sysroot(); + + // `-fms-runtime-lib=static` so a `#[cc:]` object matches the libcmt + // Backend always links, then `-isystem` for every directory under + // sysroot/include. the CRT flag is independent of the bundle; the + // includes no-op when there is no sysroot, so a developer build still + // uses the VS that clang finds on its own + void append_windows_sysroot_cc_args(std::vector &argv); + + // `-fuse-ld=lld`, static CRT, and `-Lsysroot/lib` so the clang fallback + // and a `#[cc:]` shared library hit the bundled linker and libcmt, not + // PATH's DLL CRT + void append_windows_sysroot_link_args(std::vector &argv); + + // the same spawn, capturing merged stdout+stderr instead of inheriting them. + // + // a test's output belongs under its own row, and a corpus case's golden is a byte comparison, so + // neither can inherit the streams the way clang's diagnostics do. `timeout_ms` of 0 waits forever. + // `working_directory` empty means the caller's. `extra_env` is added to (or replaces) the child's + // copy of this process's environment, so a parallel spawn cannot leak a variable into the parent. + // `stdin_content` is written to the child's standard input and then closed, so a `readline` case + // sees end-of-file after the last line; empty inherits the caller's stdin. + // + // a fired deadline kills the process group (POSIX) or the job object (Windows), so `echoc build` + // cannot leave clang running after the parent is gone + struct CapturedProcess + { + int exit_code = 0; + std::string output; + bool timed_out = false; + + // POSIX signal number, or 0. Windows has no signals; a crash is a non-zero exit_code + int signal = 0; + }; + + CapturedProcess run_captured( + const std::vector &argv, + unsigned timeout_ms = 0, + const std::filesystem::path &working_directory = {}, + const std::vector> &extra_env = {}, + const std::string &stdin_content = {} + ); + + // a shell command, for tests that already spelled `2>&1` / `2>nul`. Windows is `cmd /c` + // with the payload unquoted as a unit; POSIX is `sh -c` + CapturedProcess run_shell(const std::string &command, unsigned timeout_ms = 0); }; #endif diff --git a/include/Compiler/LLVM/Codegen/DebugInfoCodegen.h b/include/Compiler/LLVM/Codegen/DebugInfoCodegen.h index 080ab17a..98bddccd 100644 --- a/include/Compiler/LLVM/Codegen/DebugInfoCodegen.h +++ b/include/Compiler/LLVM/Codegen/DebugInfoCodegen.h @@ -169,9 +169,10 @@ namespace Compiler::LLVM CmpUnit &cmp_unit ); - // the composite shapes, each interned through a replaceable temporary *before* its members are - // built - a struct holding a pointer to itself is otherwise an infinite recursion rather than a - // wrong answer + // the composite shapes. a struct interns a replaceable temporary before its members, because a + // field that is `ptr` back to the same type would otherwise recurse forever. a class value is a + // handle (pointer to the box), so it interns an opaque pointer for that same cycle instead - + // a pointer to a replaceable composite does not survive `replaceTemporary` llvm::DIType *struct_type_of(const AST::ValueType &type, CmpUnit &cmp_unit); llvm::DIType *class_type_of(const AST::ValueType &type, CmpUnit &cmp_unit); // a type's own properties as DIMemberTypes, appended at their offsets from the diff --git a/include/Compiler/LLVM/CodegenContext.h b/include/Compiler/LLVM/CodegenContext.h index 50e7f65e..5c2b3f6b 100644 --- a/include/Compiler/LLVM/CodegenContext.h +++ b/include/Compiler/LLVM/CodegenContext.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -350,6 +351,63 @@ namespace Compiler::LLVM name, llvm::FunctionType::get(return_type, parameter_types, variadic)); } + // the object is for this machine, so CRT names follow the triple rather than + // `--target-os`. that flag only picks `#[if:]` arms; it does not retarget + bool targeting_windows() const { + return llvm::Triple(target_triple).isOSWindows(); + } + + // `write(fd, ptr, len)` on POSIX, `_write` on Windows UCRT. length is i64 in + // IR either way; Windows truncates to i32 because that is the CRT's count + void emit_libc_write(int fd, llvm::Value *ptr, llvm::Value *len) + { + llvm::Type *i32 = llvm::Type::getInt32Ty(*llvm_context); + llvm::Type *i64 = llvm::Type::getInt64Ty(*llvm_context); + llvm::Type *opaque_ptr = opaque_ptr_type(); + llvm::Value *fd_val = llvm::ConstantInt::get(i32, fd); + + if (targeting_windows()) { + llvm::Value *count = builder->CreateTrunc(len, i32, "write.n"); + builder->CreateCall( + libc_callee("_write", i32, { i32, opaque_ptr, i32 }), + { fd_val, ptr, count }); + return; + } + + builder->CreateCall( + libc_callee("write", i64, { i32, opaque_ptr, i64 }), + { fd_val, ptr, len }); + } + + // stdout is fully buffered when it is a pipe. `echo` of a string goes through + // `_write` (unbuffered) after `fflush(NULL)`, and that fflush is a no-op from + // JIT'd code on Windows: MCJIT resolves UCRT but `fflush(NULL)` does not drain + // this process's FILE*. unbuffering stdout and stderr makes `printf` and + // `_write` the same kind of write, so program order is what the goldens record + void emit_unbuffer_stdio() + { + if (!targeting_windows()) { + return; + } + + llvm::Type *i32 = llvm::Type::getInt32Ty(*llvm_context); + llvm::Type *i64 = llvm::Type::getInt64Ty(*llvm_context); + llvm::Type *ptr = opaque_ptr_type(); + llvm::FunctionCallee iob = libc_callee("__acrt_iob_func", ptr, { i32 }); + llvm::FunctionCallee setvbuf_fn = libc_callee( + "setvbuf", i32, { ptr, ptr, i32, i64 }); + llvm::Value *null = llvm::ConstantPointerNull::get( + llvm::cast(ptr)); + llvm::Value *ionbf = llvm::ConstantInt::get(i32, 4); + llvm::Value *zero = llvm::ConstantInt::get(i64, 0); + + for (unsigned fd : { 1u, 2u }) { + llvm::Value *file = builder->CreateCall( + iob, { llvm::ConstantInt::get(i32, fd) }); + builder->CreateCall(setvbuf_fn, { file, null, ionbf, zero }); + } + } + // the opaque `ptr`, spelled once - it appears in almost every emitted runtime signature llvm::Type *opaque_ptr_type() const { return llvm::PointerType::get(*llvm_context, 0); @@ -487,6 +545,15 @@ namespace Compiler::LLVM // second into the first is how a target-less module would have started answering false bool test_mode = false; + // `main` dispatches on `ECO_INTERNAL_RUN_TEST`. only a linked runner wants that ladder; + // the JIT path calls each test by address and must not emit it, or a leftover env var + // would run a test inside the prologue + bool emit_native_test_runner = false; + + // mangled names `echoc test` will call. empty on a `run`/`build`. a native runner's + // `main` looks them up, and DCE cannot drop a test nothing in `main` appeared to reach + std::vector test_symbols; + // the main compilation unit, or nullptr if the bundle has no main module yet CmpUnit *main_cmp_unit(); diff --git a/include/Compiler/LLVM/LLVMCompiler.h b/include/Compiler/LLVM/LLVMCompiler.h index 3e15bb16..6f2a12b9 100644 --- a/include/Compiler/LLVM/LLVMCompiler.h +++ b/include/Compiler/LLVM/LLVMCompiler.h @@ -27,8 +27,8 @@ #include "Compiler/LLVM/Codegen/Backend.h" #include -#include #include +#include #include #include @@ -51,6 +51,15 @@ class LLVMCompiler : public AST::Visitor // compile_bundle - see CodegenContext::test_mode void set_test_mode(bool test_mode); + // `main` itself looks up `ECO_INTERNAL_RUN_TEST` and calls that test. only a linked runner + // needs this; the JIT path calls each test by address and must not emit the ladder + void set_native_test_runner(bool enabled); + + // the tests this compile will run, as mangled symbols. must be set before compile_bundle + // so a native runner's `main` can dispatch on them and so DCE cannot drop a test nothing + // in `main` appeared to reach + void set_test_symbols(std::vector symbols); + // `cached_modules` names the modules whose compiled object is being reused, so no code is generated for // them at all - see TypeLowering::create_cmp_units for why that is the only place it has to be said void compile_bundle(const AST::Bundle &bundle, const std::set &cached_modules = {}); @@ -81,8 +90,10 @@ class LLVMCompiler : public AST::Visitor // **a test run has no program**, so it returns having emitted nothing rather than walking and // narrowing to nothing: `main` keeps the prologue its caller emitted - a test may read // `std::env::args()` - and ends there. The tests themselves are ordinary functions the declaration - // walk already emitted, and Compiler::TestRunner calls each by its own symbol + // walk already emitted. A native runner's `main` then dispatches; the JIT path calls each + // symbol from TestRunner void emit_entry_file_roots(Compiler::LLVM::CmpUnit &main_cmp_unit); + void emit_test_dispatch(); void visitScope(AST::ScopeNode &node); void visitType(AST::TypeNode &node); diff --git a/include/Compiler/TerminalCapabilities.h b/include/Compiler/TerminalCapabilities.h index fbea8c49..daae7919 100644 --- a/include/Compiler/TerminalCapabilities.h +++ b/include/Compiler/TerminalCapabilities.h @@ -88,7 +88,9 @@ namespace Compiler // // `NO_COLOR` (any value) and `TERM=dumb` suppress; `CLICOLOR_FORCE` (non-empty, not "0") forces. // unicode is decided by the locale saying UTF-8, or by `WT_SESSION` - Windows Terminal, as opposed - // to the legacy console the same binary may be run from. + // to the legacy console the same binary may be run from. Answering yes is not enough on its own: + // Windows still decodes `_write` with the OEM page until prepare_terminal() sets the console to + // UTF-8. // // the three environment escape hatches are properties of the *invocation* and so are asked the // same way whichever stream this is about; what moves with the stream is the isatty underneath @@ -104,6 +106,14 @@ namespace Compiler static TerminalCapabilities plain(); }; + // one-time host setup, before any write. Unix has nothing to prepare - the bytes are the picture. + // Windows `_write` to a console is decoded with the OEM code page even inside Windows Terminal, so a + // UTF-8 box-drawing glyph lands as three CP850 characters (`▌` becomes `Ôûî`). Setting the console + // to UTF-8 fixes every writer (`_write`, iostreams, llvm::errs, printf); the virtual-terminal bit + // is what colour needs in the legacy console. A pipe is not a console, so a golden still + // byte-compares. + void prepare_terminal(); + // `auto` / `always` / `never` and `auto` / `pretty` / `ascii` / `json`, refused rather than defaulted // on anything else - a mistyped `--color=alwyas` that silently means `auto` is a flag the user // believes they set. False with a sentence in `out_error`, which is how TargetFacts::resolve reports diff --git a/include/Compiler/TestRunner.h b/include/Compiler/TestRunner.h index e33e3e69..22c56161 100644 --- a/include/Compiler/TestRunner.h +++ b/include/Compiler/TestRunner.h @@ -5,8 +5,11 @@ #include "Compiler/TestSelection.h" +#if defined(__unix__) || defined(__APPLE__) #include +#endif #include +#include namespace Compiler { @@ -51,34 +54,34 @@ namespace Compiler } }; - // **is running a test in a child process possible on this platform at all.** - // - // false is not a diagnostic - the caller writes that - but it exists so the refusal happens at the - // subcommand rather than at the first test, which is the same call `#[link: framework]` makes about - // Darwin: a platform that cannot do the thing is told so where the thing was asked for - bool test_isolation_available(); + // the environment variable a linked test runner looks up to decide which test to call. + // the parent sets it per child so a parallel spawn cannot leak the name into this process + constexpr const char *k_isolated_test_env = "ECO_INTERNAL_RUN_TEST"; - // **runs one test in a forked child and reports how it ended.** - // - // fork rather than anything in-process, and that is the design: a failed `assert` lowers to - // `__eco_abort`, which writes its message and calls `exit(1)`. There is no unwind, no landing pad and no - // signal handler anywhere in this compiler or in the standard library, so "continue after a failure" is - // either a new kind of assertion that skips every destructor between itself and the runner, or a process - // boundary. A boundary survives `assert`, `die`, `env::exit` and a segfault alike, and changes nothing - // about how any of them lower. + // **runs one test in a child process and reports how it ended.** // - // `call` is the test's body, reached through its address in the JIT - the child's whole job. It runs - // **only in the child**, which is what makes the parent's own state irrelevant to it. + // a process boundary rather than anything in-process, and that is the design: a failed `assert` + // lowers to `__eco_abort`, which writes its message and calls `exit(1)`. There is no unwind, no + // landing pad and no signal handler anywhere in this compiler or in the standard library, so + // "continue after a failure" is either a new kind of assertion that skips every destructor + // between itself and the runner, or a process boundary. A boundary survives `assert`, `die`, + // `env::exit` and a segfault alike, and changes nothing about how any of them lower. // - // the parent flushes its streams before forking, or whatever it had buffered is duplicated into every - // child and written again by each - // `timeout_ms` is `--timeout`. zero waits forever. the parent watches the clock with - // poll and SIGKILL, so the flag's unit is honest and a child that ignores SIGALRM - // still dies. a fired deadline is `t_timed_out`, not `t_signalled` + // two ways to start the child, one result shape. the JIT path forks and calls `call` in the + // child. a linked runner is spawned as `argv` with `ECO_INTERNAL_RUN_TEST` naming this test. + // `timeout_ms` is `--timeout`. zero waits forever. a fired deadline is `t_timed_out` + +#if defined(__unix__) || defined(__APPLE__) TestResult run_test_isolated( const TestCase &test, const std::function &call, unsigned timeout_ms = 0); +#endif + + TestResult run_test_isolated( + const TestCase &test, + const std::vector &argv, + unsigned timeout_ms = 0); }; #endif diff --git a/include/eco.h b/include/eco.h index 32c7ab63..5778f79b 100644 --- a/include/eco.h +++ b/include/eco.h @@ -78,12 +78,12 @@ // automatically - this constant covers the one input nothing can detect: a change to this compiler's own // lowering. Forgetting it means a stale object silently linked into a new build, which is the single failure // mode in the cache with no diagnostic -#define ECO_MODULE_CACHE_VERSION "58" +#define ECO_MODULE_CACHE_VERSION "61" // the same knob for the C object cache, and a separate one because the two caches are separate stores // keyed on unrelated inputs: a codegen change moves every Echo object and no C one, and a change to how // `#[cc:]` builds a translation unit - a flag added, the language inference changed - moves every C object // and no Echo one. Sharing a constant would rebuild the wrong half every time either moved -#define ECO_C_BUILD_VERSION "1" +#define ECO_C_BUILD_VERSION "3" #endif // ECO_H diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 00000000..8489351b --- /dev/null +++ b/install.ps1 @@ -0,0 +1,182 @@ +# installs the latest released echoc and epm into %LOCALAPPDATA%\echo: +# +# irm https://raw.githubusercontent.com/echolang/echo/master/install.ps1 | iex +# +# a released echoc carries the standard library inside it. epm is the package +# manager and sits next to it. clang, lld-link and a Windows sysroot ship in +# the same archive, so `echoc build` works without a separate LLVM install. +# set ECHO_INSTALL_DIR to install somewhere other than %LOCALAPPDATA%\echo. +# set ECHO_UNINSTALL=1 (or pass -Uninstall when invoking the file) to remove it. + +$ErrorActionPreference = "Stop" + +$Repo = "echolang/echo" +$Uninstall = $false +if ($args -contains "-Uninstall") { + $Uninstall = $true +} +if ($env:ECHO_UNINSTALL -eq "1") { + $Uninstall = $true +} + +function Get-EchoInstallRoot { + if ($env:ECHO_INSTALL_DIR -and $env:ECHO_INSTALL_DIR.Trim().Length -gt 0) { + return $env:ECHO_INSTALL_DIR.TrimEnd("\", "/") + } + return Join-Path $env:LOCALAPPDATA "echo" +} + +function Get-EchoBinDir([string]$Root) { + return Join-Path $Root "bin" +} + +function Broadcast-Environment { + try { + Add-Type -Namespace EchoInstall -Name Native -MemberDefinition @" + [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); +"@ -ErrorAction SilentlyContinue + $HWND_BROADCAST = [IntPtr]0xffff + $WM_SETTINGCHANGE = 0x1a + $result = [UIntPtr]::Zero + [void][EchoInstall.Native]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::Zero, "Environment", 2, 5000, [ref]$result) + } + catch { + } +} + +function Get-UserPath { + $path = [Environment]::GetEnvironmentVariable("Path", "User") + if ($null -eq $path) { + return "" + } + return $path +} + +function Set-UserPath([string]$Path) { + $key = "HKCU:\Environment" + if (-not (Test-Path -LiteralPath $key)) { + New-Item -Path $key -Force | Out-Null + } + $existing = Get-ItemProperty -LiteralPath $key -Name Path -ErrorAction SilentlyContinue + if ($null -eq $existing) { + New-ItemProperty -LiteralPath $key -Name Path -Value $Path -PropertyType ExpandString | Out-Null + } + else { + Set-ItemProperty -LiteralPath $key -Name Path -Value $Path -Type ExpandString + } + Broadcast-Environment +} + +function Add-UserPath([string]$Dir) { + $current = Get-UserPath + $parts = @() + if ($current.Length -gt 0) { + $parts = $current.Split(";", [System.StringSplitOptions]::RemoveEmptyEntries) + } + foreach ($part in $parts) { + if ($part.TrimEnd("\", "/").ToLowerInvariant() -eq $Dir.TrimEnd("\", "/").ToLowerInvariant()) { + return + } + } + if ($current.Length -eq 0) { + Set-UserPath $Dir + } + else { + Set-UserPath ($current.TrimEnd(";") + ";" + $Dir) + } +} + +function Remove-UserPath([string]$Dir) { + $current = Get-UserPath + if ($current.Length -eq 0) { + return + } + $want = $Dir.TrimEnd("\", "/").ToLowerInvariant() + $kept = @() + foreach ($part in $current.Split(";", [System.StringSplitOptions]::RemoveEmptyEntries)) { + if ($part.TrimEnd("\", "/").ToLowerInvariant() -ne $want) { + $kept += $part + } + } + Set-UserPath ($kept -join ";") +} + +$InstallRoot = Get-EchoInstallRoot +$BinDir = Get-EchoBinDir $InstallRoot + +if ($Uninstall) { + if (Test-Path -LiteralPath $InstallRoot) { + Remove-Item -LiteralPath $InstallRoot -Recurse -Force + } + Remove-UserPath $BinDir + Write-Host "uninstalled Echo from $InstallRoot" + exit 0 +} + +$arch = $env:PROCESSOR_ARCHITECTURE +if ($env:PROCESSOR_ARCHITEW6432) { + $arch = $env:PROCESSOR_ARCHITEW6432 +} +if ($arch -ne "AMD64") { + Write-Error "echo: no prebuilt Echo for windows $arch.`nbuild it from source instead: https://github.com/$Repo" + exit 1 +} + +$asset = "echo-windows-x86_64" +$url = "https://github.com/$Repo/releases/latest/download/$asset.zip" +$tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("echo-install-" + [guid]::NewGuid().ToString("n")) +New-Item -ItemType Directory -Path $tmp | Out-Null + +try { + Write-Host "downloading $asset ..." + $zip = Join-Path $tmp "$asset.zip" + try { + Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing + } + catch { + Write-Error "echo: could not download $url`nsee https://github.com/$Repo/releases for what is available." + exit 1 + } + + Expand-Archive -LiteralPath $zip -DestinationPath $tmp -Force + + $extractedBin = Join-Path $tmp "bin" + if (-not (Test-Path -LiteralPath (Join-Path $extractedBin "echoc.exe"))) { + Write-Error "echo: the downloaded archive does not contain bin\echoc.exe." + exit 1 + } + if (-not (Test-Path -LiteralPath (Join-Path $extractedBin "epm.exe"))) { + Write-Error "echo: the downloaded archive does not contain bin\epm.exe." + exit 1 + } + + New-Item -ItemType Directory -Path $InstallRoot -Force | Out-Null + foreach ($name in @("bin", "lib", "sysroot")) { + $src = Join-Path $tmp $name + if (Test-Path -LiteralPath $src) { + $dest = Join-Path $InstallRoot $name + if (Test-Path -LiteralPath $dest) { + Remove-Item -LiteralPath $dest -Recurse -Force + } + Copy-Item -LiteralPath $src -Destination $dest -Recurse -Force + } + } + foreach ($name in @("README.md", "LICENSE")) { + $src = Join-Path $tmp $name + if (Test-Path -LiteralPath $src) { + Copy-Item -LiteralPath $src -Destination (Join-Path $InstallRoot $name) -Force + } + } + + Add-UserPath $BinDir + + $echocVersion = & (Join-Path $BinDir "echoc.exe") --version + $epmVersion = & (Join-Path $BinDir "epm.exe") --version + Write-Host "installed echoc $echocVersion to $(Join-Path $BinDir 'echoc.exe')" + Write-Host "installed epm $epmVersion to $(Join-Path $BinDir 'epm.exe')" + Write-Host "open a new terminal so PATH picks this up." +} +finally { + Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue +} diff --git a/install.sh b/install.sh index 0704b0ab..27fac066 100755 --- a/install.sh +++ b/install.sh @@ -14,7 +14,7 @@ REPO="echolang/echo" INSTALL_DIR="${ECHO_INSTALL_DIR:-/usr/local/bin}" # spelled out rather than derived: uname says Darwin where the release asset says macos, and these -# two are the only platforms the release workflow builds +# three are the platforms the release workflow builds (windows is install.ps1) case "$(uname -s)-$(uname -m)" in Darwin-arm64) asset="echo-macos-arm64" ;; Linux-x86_64) asset="echo-linux-x86_64" ;; diff --git a/src/AST/ASTLiteralTyping.cpp b/src/AST/ASTLiteralTyping.cpp index 51b1f9e0..68a68bf4 100644 --- a/src/AST/ASTLiteralTyping.cpp +++ b/src/AST/ASTLiteralTyping.cpp @@ -473,8 +473,22 @@ namespace AST return out; } - const ValueType left = lhs->result_type(); - const ValueType right = rhs->result_type(); + // a borrow of a number is a number once it is read. PointerAdjuster peels it later; asked + // here so `$i == 0` over a range element (`const usize&`) retypes the literal instead of + // reaching codegen as i64 vs i32. a `ptr` is nullable and is an address, not a width - + // leave it, so `$p + n` and `$p:$ + n` stay pointer arithmetic + const auto operand_type = [](const ExprNode &expr) { + const ValueType raw = expr.result_type(); + + if (raw.is_pointer() && !raw.is_nullable()) { + return value_type_of(raw); + } + + return raw; + }; + + const ValueType left = operand_type(*lhs); + const ValueType right = operand_type(*rhs); const bool left_defers = is_untyped_literal(lhs); const bool right_defers = is_untyped_literal(rhs); @@ -550,6 +564,13 @@ namespace AST return out; } + // converting an address to an integer is pointer arithmetic's opposite, and a cast of + // `const usize&` to `int32` would do that. a literal beside a borrow is retyped above; + // two typed integers where one is still a borrow wait for PointerAdjuster + if (loser->result_type().is_pointer()) { + return out; + } + (left_loses ? out.lhs : out.rhs) = &nodes.emplace_back(*common, loser, true); out.result = BinaryReconciliation::Result::t_reconciled; diff --git a/src/AST/ASTModuleEmbedder.cpp b/src/AST/ASTModuleEmbedder.cpp index aad7e68e..c7db6927 100644 --- a/src/AST/ASTModuleEmbedder.cpp +++ b/src/AST/ASTModuleEmbedder.cpp @@ -1,8 +1,75 @@ #include "AST/ASTModuleEmbedder.h" +#include "AST/ASTModule.h" +#include #include +#include +#include +#include + #include -#include "AST/ASTModule.h" + +namespace +{ + +// `path.generic_string()` is not enough: on POSIX a string built with backslashes +// is one filename, and the Windows release path is native `\` against a CMake +// STDLIB_SOURCE_DIR that uses `/`. one spelling, so the prefix match cannot miss +std::string with_forward_slashes(std::string path) +{ + for (char &c : path) { + if (c == '\\') { + c = '/'; + } + } + return path; +} + +std::string as_c_string_literal(const std::string &value) +{ + std::string out = "\""; + for (unsigned char c : value) { + switch (c) { + case '\\': + out += "\\\\"; + break; + case '"': + out += "\\\""; + break; + case '\n': + out += "\\n"; + break; + default: + out += static_cast(c); + break; + } + } + out += '"'; + return out; +} + +}; + +std::string AST::embedded_source_path(const std::filesystem::path &path) +{ + std::string file_path = with_forward_slashes(path.generic_string()); + std::string stdlib_root = with_forward_slashes( + std::filesystem::path(STDLIB_SOURCE_DIR).generic_string()); + + // drop a trailing slash so the remainder keeps its leading one, matching the + // existing `stdlib:/core/...` spelling + while (!stdlib_root.empty() && stdlib_root.back() == '/') { + stdlib_root.pop_back(); + } + + if (file_path.size() >= stdlib_root.size() + && file_path.compare(0, stdlib_root.size(), stdlib_root) == 0 + && (file_path.size() == stdlib_root.size() || file_path[stdlib_root.size()] == '/')) { + file_path = "stdlib:" + file_path.substr(stdlib_root.size()); + } + + return file_path; +} void AST::write_embedded_module(AST::Module &module, const std::string &output_path) { @@ -23,16 +90,9 @@ void AST::write_embedded_module(AST::Module &module, const std::string &output_p for (auto &file : module.files()) { file_index++; std::string filevar = fmt::format("file_{}", file_index); + std::string file_path = embedded_source_path(file.get_path()); - // this is not really clean, but it works for now - // we can take the "STDLIB_SOURCE_DIR" define to determine the relative path - // of the full file path if it begins with it - std::string file_path = file.get_path().string(); - if (file_path.find(STDLIB_SOURCE_DIR) == 0) { - file_path = "stdlib:" + file_path.substr(strlen(STDLIB_SOURCE_DIR)); - } - - output << fmt::format(" auto &{} = module.add_file(\"{}\");\n", filevar, file_path); + output << fmt::format(" auto &{} = module.add_file({});\n", filevar, as_c_string_literal(file_path)); output << fmt::format(" static const unsigned char {}_data[] = {{\n", filevar); output << " "; diff --git a/src/AST/ASTOperatorRewriter.cpp b/src/AST/ASTOperatorRewriter.cpp index f96ce57d..55538674 100644 --- a/src/AST/ASTOperatorRewriter.cpp +++ b/src/AST/ASTOperatorRewriter.cpp @@ -135,12 +135,14 @@ void OperatorRewriter::widen_binary_operands(BinaryExprNode &bin) // **an address is never a width to reconcile.** pointer arithmetic and pointer comparison each have // their own arm in BinaryExprNode::result_type(), and a cast inserted here would convert the address // itself to an integer - which is what `string::view($this->bytes:$ + $from, $len)` turns into if this - // exits any later. Asked of the *raw* operand types, because this pass runs ahead of - // AST::PointerAdjuster and a borrow still reads as a pointer here + // exits any later. asked of the *nullable* half, because this pass runs ahead of AST::PointerAdjuster + // and a borrow still reads as a pointer here - and a `const usize&` foreach element compared to a + // literal is that borrow, not an address. `ptr` is the skip; `T&` is a number once it is read const ValueType raw_left = bin.lhs->result_type(); const ValueType raw_right = bin.rhs->result_type(); - if (raw_left.is_pointer() || raw_right.is_pointer()) { + if ((raw_left.is_pointer() && raw_left.is_nullable()) + || (raw_right.is_pointer() && raw_right.is_nullable())) { return; } diff --git a/src/Compiler/BuildLayout.cpp b/src/Compiler/BuildLayout.cpp index 84a2df5d..8acaa09f 100644 --- a/src/Compiler/BuildLayout.cpp +++ b/src/Compiler/BuildLayout.cpp @@ -35,13 +35,24 @@ std::filesystem::path marker_path(const std::filesystem::path &directory) return directory / "CACHEDIR.TAG"; } -// `$XDG_CACHE_HOME/echo`, else `$HOME/.cache/echo`, else empty when neither is set +// `$XDG_CACHE_HOME/echo`, else `$HOME/.cache/echo`, else empty when neither is set. +// Windows has no HOME by default: `%LOCALAPPDATA%\echo`, else `%USERPROFILE%\.cache\echo` std::filesystem::path user_cache_root() { if (const char *xdg = std::getenv("XDG_CACHE_HOME"); xdg != nullptr && *xdg != '\0') { return std::filesystem::path(xdg) / "echo"; } +#if defined(_WIN32) + if (const char *local = std::getenv("LOCALAPPDATA"); local != nullptr && *local != '\0') { + return std::filesystem::path(local) / "echo"; + } + + if (const char *profile = std::getenv("USERPROFILE"); profile != nullptr && *profile != '\0') { + return std::filesystem::path(profile) / ".cache" / "echo"; + } +#endif + if (const char *home = std::getenv("HOME"); home != nullptr && *home != '\0') { return std::filesystem::path(home) / ".cache" / "echo"; } @@ -67,7 +78,10 @@ bool is_empty_or_absent(const std::filesystem::path &directory) // questions here, which differ only in whether the paths they compare exist yet bool relative_stays_inside(const std::filesystem::path &relative) { - return !relative.empty() && relative.native().rfind("..", 0) != 0; + // first component, not a byte prefix of `.native()`: on Windows that string is + // `wchar_t` and `".."` does not convert, while `path("..")` is the same element + // on every platform + return !relative.empty() && *relative.begin() != std::filesystem::path(".."); } }; diff --git a/src/Compiler/CBuild.cpp b/src/Compiler/CBuild.cpp index adef496f..9b1bb2bb 100644 --- a/src/Compiler/CBuild.cpp +++ b/src/Compiler/CBuild.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace { @@ -123,6 +124,8 @@ bool c_spec_digest( digest = Compiler::fnv1a64( options.emitting_debug_info() ? std::string("g") : std::string("nog"), digest); + digest = Compiler::fnv1a64(Compiler::windows_sysroot().string(), digest); + for (const std::filesystem::path &include : spec.includes) { digest = Compiler::fnv1a64(include.string(), digest); } @@ -515,16 +518,24 @@ bool Compiler::build_c_sources( std::vector argv = { "clang", "-c", + }; - // one object serves the executable and the loadable library both - see the header - "-fPIC", - "-o", object.string(), - source.string(), + // PIC is the Unix loadable-object rule. Windows objects are already relocatable; + // clang-cl rejects `-fPIC` + if (TargetFacts::host().operating_system != "windows") { + argv.push_back("-fPIC"); + } - // what lets the key above see this translation unit's headers, from the next build onward - "-MD", - "-MF", depfile.string(), - }; + Compiler::append_windows_sysroot_cc_args(argv); + + argv.push_back("-o"); + argv.push_back(object.string()); + argv.push_back(source.string()); + + // what lets the key above see this translation unit's headers, from the next build onward + argv.push_back("-MD"); + argv.push_back("-MF"); + argv.push_back(depfile.string()); append_common_arguments(spec, options, argv); @@ -597,6 +608,7 @@ bool Compiler::build_c_shared_library( // considerably fussier than an executable's, and this path is not the one Backend::link_executable's // fast path exists to speed up - it runs once per module per change, not once per build std::vector argv = { "clang", "-shared", "-o", out_library.string() }; + Compiler::append_windows_sysroot_link_args(argv); for (const std::filesystem::path &object : objects) { argv.push_back(object.string()); @@ -604,6 +616,53 @@ bool Compiler::build_c_shared_library( argv.insert(argv.end(), link_words.begin(), link_words.end()); +#if defined(_WIN32) + // COFF hides symbols unless they are exported. Unix .so files export everything + // defined; without a .def the JIT's LoadLibrary finds the DLL and none of the + // functions, and the first call is an access violation. llvm-nm lists the + // defined symbols; `/DEF:` is how lld-link is told to export them + const std::filesystem::path def_path = out_library.string() + ".def"; + std::vector nm_argv = { "llvm-nm", "--extern-only", "--defined-only" }; + + for (const std::filesystem::path &object : objects) { + nm_argv.push_back(object.string()); + } + + const CapturedProcess nm = run_captured(nm_argv); + + if (nm.exit_code != 0) { + out_error = fmt::format( + "listing C object symbols for module '{}' failed.", spec.module_name); + return false; + } + + std::ofstream def(def_path); + + if (!def) { + out_error = fmt::format( + "could not write the export list for module '{}' at '{}'.", + spec.module_name, def_path.string()); + return false; + } + + def << "EXPORTS\n"; + + for (const std::string &name : coff_exports_from_nm(nm.output)) { + def << name << "\n"; + } + + def.flush(); + + if (!def) { + out_error = fmt::format( + "could not write the export list for module '{}' at '{}'.", + spec.module_name, def_path.string()); + return false; + } + + argv.push_back("-Wl,/DEF:" + def_path.string()); +#endif + if (!run_tool(argv)) { out_error = fmt::format( "linking the C sources of module '{}' into a loadable library failed.", spec.module_name); @@ -612,3 +671,43 @@ bool Compiler::build_c_shared_library( return true; } + +std::vector Compiler::coff_exports_from_nm(const std::string &nm_output) +{ + std::vector names; + std::istringstream lines(nm_output); + std::string line; + + while (std::getline(lines, line)) { + if (line.empty() || line.back() == ':') { + continue; + } + + std::istringstream fields(line); + std::string address; + std::string type; + std::string name; + + if (!(fields >> address >> type >> name)) { + continue; + } + + if (type.size() != 1) { + continue; + } + + const char kind = type[0]; + + if (kind != 'T' && kind != 'D' && kind != 'B' && kind != 'R') { + continue; + } + + if (name.empty() || name[0] == '.' || name[0] == '?') { + continue; + } + + names.push_back(name); + } + + return names; +} diff --git a/src/Compiler/HostTool.cpp b/src/Compiler/HostTool.cpp index 3d48911c..ef5a3f01 100644 --- a/src/Compiler/HostTool.cpp +++ b/src/Compiler/HostTool.cpp @@ -2,25 +2,431 @@ #include "Compiler/ProgressReporter.h" +#include #include +#include -bool Compiler::run_tool(const std::vector &argv) +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#include +#include +#include +#include +#endif + +namespace +{ + std::string sibling_tool(const std::filesystem::path &clang, const std::string &name) + { + std::filesystem::path candidate = clang.parent_path() / name; + +#if defined(_WIN32) + if (candidate.extension().empty()) { + candidate += ".exe"; + } +#endif + + std::error_code ec; + if (std::filesystem::is_regular_file(candidate, ec)) { + return candidate.string(); + } + + return {}; + } + + std::string resolve_program(const std::string &name) + { + // a released Windows echoc ships clang and lld-link next to itself. + // look there before the baked build-machine path, which does not + // exist on a machine that only ran the installer + const std::string bundled = sibling_tool(Compiler::process_directory(), name); + if (!bundled.empty()) { + return bundled; + } + + // prefer the LLVM that built echoc. findProgramByName walks PATH, and + // llvm-mingw's GNU-ABI clang is a common first hit on Windows +#ifdef ECO_HOST_CLANG + const std::filesystem::path baked(ECO_HOST_CLANG); + std::error_code ec; + + if (name == "clang" || name == "clang.exe") { + if (std::filesystem::is_regular_file(baked, ec)) { + return baked.string(); + } + } + else { + const std::string sibling = sibling_tool(baked, name); + if (!sibling.empty()) { + return sibling; + } + } +#endif + + llvm::ErrorOr program = llvm::sys::findProgramByName(name); + + if (!program) { + return name; + } + + return program.get(); + } + +#if defined(_WIN32) + std::wstring widen(const std::string &text) + { + if (text.empty()) { + return std::wstring(); + } + + const int needed = MultiByteToWideChar( + CP_UTF8, 0, text.data(), static_cast(text.size()), nullptr, 0); + + if (needed <= 0) { + return std::wstring(); + } + + std::wstring wide(static_cast(needed), L'\0'); + MultiByteToWideChar( + CP_UTF8, 0, text.data(), static_cast(text.size()), wide.data(), needed); + return wide; + } + + std::string narrow(const wchar_t *text, size_t length) + { + if (text == nullptr || length == 0) { + return std::string(); + } + + const int needed = WideCharToMultiByte( + CP_UTF8, 0, text, static_cast(length), nullptr, 0, nullptr, nullptr); + + if (needed <= 0) { + return std::string(); + } + + std::string utf8(static_cast(needed), '\0'); + WideCharToMultiByte( + CP_UTF8, 0, text, static_cast(length), utf8.data(), needed, nullptr, nullptr); + return utf8; + } + + // CommandLineToArgvW: n backslashes before a non-quote stay n; n before a quote + // become 2n+1 and the quote is literal; n at the end of a quoted word become 2n + // so the closing delimiter is not eaten. empty words are `""` + std::wstring quote_windows_arg_wide(const std::string &arg) + { + const std::wstring wide = widen(arg); + const bool needs_quotes = + wide.empty() || wide.find_first_of(L" \t\"") != std::wstring::npos; + + if (!needs_quotes) { + return wide; + } + + std::wstring quoted; + quoted.push_back(L'"'); + size_t backslashes = 0; + + for (wchar_t c : wide) { + if (c == L'\\') { + ++backslashes; + continue; + } + + if (c == L'"') { + quoted.append(backslashes * 2 + 1, L'\\'); + quoted.push_back(L'"'); + backslashes = 0; + continue; + } + + quoted.append(backslashes, L'\\'); + backslashes = 0; + quoted.push_back(c); + } + + quoted.append(backslashes * 2, L'\\'); + quoted.push_back(L'"'); + return quoted; + } + + std::wstring windows_command_line(const std::vector &argv) + { + // cmd's `/c` remainder is a command string, not an argv word. CommandLineToArgvW + // quoting a payload that already contains quotes (`"C:\path\echoc.exe" --help`) + // is the encoding cmd then misreads as a program named `\"C:\path\echoc.exe\"` + if (argv.size() >= 3) { + const std::wstring first = quote_windows_arg_wide(argv[0]); + if (argv[1] == "/c" + && (first == L"cmd.exe" || first.ends_with(L"\\cmd.exe") || first.ends_with(L"/cmd.exe"))) { + return first + L" /c " + widen(argv[2]); + } + } + + std::wstring line; + + for (const std::string &arg : argv) { + if (!line.empty()) { + line += L' '; + } + + line += quote_windows_arg_wide(arg); + } + + return line; + } + + std::wstring windows_environment(const std::vector> &extra_env) + { + if (extra_env.empty()) { + return std::wstring(); + } + + const wchar_t *block = GetEnvironmentStringsW(); + + if (block == nullptr) { + return std::wstring(); + } + + std::vector entries; + const wchar_t *cursor = block; + + while (*cursor != L'\0') { + const std::wstring entry(cursor); + cursor += entry.size() + 1; + + const size_t eq = entry.find(L'='); + + if (eq == std::wstring::npos || eq == 0) { + continue; + } + + bool replaced = false; + const std::string key = narrow(entry.c_str(), eq); + + for (const auto &pair : extra_env) { + if (pair.first == key) { + replaced = true; + break; + } + } + + if (!replaced) { + entries.push_back(entry); + } + } + + FreeEnvironmentStringsW(const_cast(block)); + + for (const auto &pair : extra_env) { + entries.push_back(widen(pair.first) + L'=' + widen(pair.second)); + } + + std::wstring merged; + + for (const std::wstring &entry : entries) { + merged += entry; + merged += L'\0'; + } + + merged += L'\0'; + return merged; + } +#else + std::string drain_fd(int fd, unsigned timeout_ms, bool &timed_out) + { + timed_out = false; + std::string collected; + char buffer[4096]; + const auto started = std::chrono::steady_clock::now(); + + while (true) { + int wait_ms = -1; + + if (timeout_ms > 0) { + const auto elapsed = std::chrono::duration_cast( + std::chrono::steady_clock::now() - started).count(); + + if (elapsed >= static_cast(timeout_ms)) { + timed_out = true; + return collected; + } + + wait_ms = static_cast(static_cast(timeout_ms) - elapsed); + } + + pollfd pfd{}; + pfd.fd = fd; + pfd.events = POLLIN; + + const int ready = poll(&pfd, 1, wait_ms); + + if (ready < 0) { + if (errno == EINTR) { + continue; + } + + return collected; + } + + if (ready == 0) { + timed_out = true; + return collected; + } + + const ssize_t read_bytes = read(fd, buffer, sizeof(buffer)); + + if (read_bytes > 0) { + collected.append(buffer, static_cast(read_bytes)); + continue; + } + + if (read_bytes < 0 && errno == EINTR) { + continue; + } + + return collected; + } + } +#endif +}; + +std::string Compiler::host_clang() +{ + return resolve_program("clang"); +} + +std::filesystem::path Compiler::process_directory() +{ + static const std::filesystem::path dir = [] { +#if defined(_WIN32) + std::wstring buf(32768, L'\0'); + const DWORD n = GetModuleFileNameW(nullptr, buf.data(), static_cast(buf.size())); + if (n == 0 || n >= buf.size()) { + return std::filesystem::current_path(); + } + buf.resize(n); + return std::filesystem::path(buf).parent_path(); +#else + const std::string exe = llvm::sys::fs::getMainExecutable( + "echoc", reinterpret_cast(&Compiler::process_directory)); + if (exe.empty()) { + return std::filesystem::current_path(); + } + return std::filesystem::path(exe).parent_path(); +#endif + }(); + + return dir; +} + +std::filesystem::path Compiler::windows_sysroot() +{ +#if !defined(_WIN32) + return {}; +#else + static const std::filesystem::path root = [] { + std::error_code ec; + const std::filesystem::path dir = Compiler::process_directory(); + const std::filesystem::path candidates[] = { + dir / "sysroot", + dir.parent_path() / "sysroot", + }; + + for (const std::filesystem::path &candidate : candidates) { + if (std::filesystem::is_directory(candidate / "lib", ec)) { + return candidate; + } + } + + return std::filesystem::path(); + }(); + + return root; +#endif +} + +void Compiler::append_windows_sysroot_cc_args(std::vector &argv) +{ +#if !defined(_WIN32) + (void)argv; +#else + // Backend::host_linker_command always `/defaultlib:libcmt`. clang's + // default on Windows is the DLL CRT, which is how a `#[cc:]` object + // (or a static libcurl built /MD) pulled ucrt.lib in next to libucrt + // and duplicate-symbol'd malloc. asked even when there is no sysroot, + // because the CRT choice is the linker's, not the bundle's + argv.push_back("-fms-runtime-lib=static"); + + const std::filesystem::path sysroot = windows_sysroot(); + if (sysroot.empty()) { + return; + } + + const std::filesystem::path include = sysroot / "include"; + std::error_code ec; + if (!std::filesystem::is_directory(include, ec)) { + return; + } + + std::vector dirs = { include }; + for (const auto &entry : std::filesystem::directory_iterator(include, ec)) { + if (entry.is_directory(ec)) { + dirs.push_back(entry.path()); + } + } + + std::sort(dirs.begin(), dirs.end()); + for (const std::filesystem::path &dir : dirs) { + argv.push_back("-isystem"); + argv.push_back(dir.string()); + } +#endif +} + +void Compiler::append_windows_sysroot_link_args(std::vector &argv) +{ +#if !defined(_WIN32) + (void)argv; +#else + argv.push_back("-fuse-ld=lld"); + argv.push_back("-fms-runtime-lib=static"); + const std::filesystem::path sysroot = windows_sysroot(); + if (sysroot.empty()) { + return; + } + argv.push_back("-L" + (sysroot / "lib").string()); +#endif +} + +int Compiler::run_wait(const std::string &program, const std::vector &argv) { if (argv.empty()) { - return false; + return 127; } - llvm::ErrorOr program = llvm::sys::findProgramByName(argv.front()); + std::error_code ec; - if (!program) { - return false; + if (!std::filesystem::is_regular_file(program, ec)) { + return 127; } std::vector args(argv.begin(), argv.end()); - // the resolved path rather than the name, so the child sees the same program findProgramByName picked - args.front() = program.get(); - // **the obligation that comes with inheriting the streams.** The child is about to write into the // same stderr a progress row may be sitting on, and there is no lock either side can take because the // child is another process. Discharged here rather than at each call site for the reason the header @@ -32,5 +438,384 @@ bool Compiler::run_tool(const std::vector &argv) // of their own ProgressReporter::instance().suspend(); - return llvm::sys::ExecuteAndWait(program.get(), args) == 0; + std::string error; + bool failed = false; + const int status = llvm::sys::ExecuteAndWait( + program, args, std::nullopt, {}, 0, 0, &error, &failed); + + if (failed) { + llvm::errs() << "could not start '" << program << "'"; + if (!error.empty()) { + llvm::errs() << ": " << error; + } + llvm::errs() << '\n'; + return 127; + } + + return status; +} + +int Compiler::run_wait(const std::vector &argv) +{ + if (argv.empty()) { + return 127; + } + + std::vector resolved = argv; + resolved.front() = resolve_program(argv.front()); + return run_wait(resolved.front(), resolved); +} + +bool Compiler::run_tool(const std::vector &argv) +{ + return run_wait(argv) == 0; +} + +#if defined(_WIN32) +std::string Compiler::quote_windows_arg(const std::string &arg) +{ + const std::wstring quoted = quote_windows_arg_wide(arg); + return narrow(quoted.c_str(), quoted.size()); +} +#endif + +Compiler::CapturedProcess Compiler::run_captured( + const std::vector &argv, + unsigned timeout_ms, + const std::filesystem::path &working_directory, + const std::vector> &extra_env, + const std::string &stdin_content +) +{ + CapturedProcess result; + + if (argv.empty()) { + result.exit_code = 127; + result.output = "run_captured: empty argv\n"; + return result; + } + + ProgressReporter::instance().suspend(); + + const std::string program = resolve_program(argv.front()); + std::vector resolved = argv; + resolved.front() = program; + +#if defined(_WIN32) + SECURITY_ATTRIBUTES inherit{}; + inherit.nLength = sizeof(inherit); + inherit.bInheritHandle = TRUE; + + HANDLE read_pipe = INVALID_HANDLE_VALUE; + HANDLE write_pipe = INVALID_HANDLE_VALUE; + + if (!CreatePipe(&read_pipe, &write_pipe, &inherit, 0)) { + result.exit_code = 127; + result.output = "could not open a pipe to capture the child's output.\n"; + return result; + } + + SetHandleInformation(read_pipe, HANDLE_FLAG_INHERIT, 0); + + HANDLE stdin_read = INVALID_HANDLE_VALUE; + HANDLE stdin_write = INVALID_HANDLE_VALUE; + + if (!stdin_content.empty() && !CreatePipe(&stdin_read, &stdin_write, &inherit, 0)) { + CloseHandle(read_pipe); + CloseHandle(write_pipe); + result.exit_code = 127; + result.output = "could not open a pipe to feed the child's input.\n"; + return result; + } + + if (stdin_write != INVALID_HANDLE_VALUE) { + SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0); + } + + STARTUPINFOW startup{}; + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESTDHANDLES; + startup.hStdInput = stdin_read != INVALID_HANDLE_VALUE + ? stdin_read + : GetStdHandle(STD_INPUT_HANDLE); + startup.hStdOutput = write_pipe; + startup.hStdError = write_pipe; + + std::wstring command = windows_command_line(resolved); + std::wstring wprogram = std::filesystem::path(program).wstring(); + std::wstring directory = working_directory.empty() ? std::wstring() : working_directory.wstring(); + std::wstring environment = windows_environment(extra_env); + + HANDLE job = CreateJobObjectW(nullptr, nullptr); + + if (job != nullptr) { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits{}; + limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + + if (!SetInformationJobObject( + job, JobObjectExtendedLimitInformation, &limits, sizeof(limits))) { + CloseHandle(job); + job = nullptr; + } + } + + PROCESS_INFORMATION process{}; + DWORD flags = CREATE_UNICODE_ENVIRONMENT; + + if (job != nullptr) { + flags |= CREATE_SUSPENDED; + } + + const BOOL created = CreateProcessW( + wprogram.c_str(), + command.data(), + nullptr, + nullptr, + TRUE, + flags, + environment.empty() ? nullptr : environment.data(), + directory.empty() ? nullptr : directory.c_str(), + &startup, + &process); + + CloseHandle(write_pipe); + + if (stdin_read != INVALID_HANDLE_VALUE) { + CloseHandle(stdin_read); + } + + if (!created) { + CloseHandle(read_pipe); + + if (stdin_write != INVALID_HANDLE_VALUE) { + CloseHandle(stdin_write); + } + + if (job != nullptr) { + CloseHandle(job); + } + + result.exit_code = 127; + result.output = "could not start '" + program + "'.\n"; + return result; + } + + if (job != nullptr) { + if (!AssignProcessToJobObject(job, process.hProcess)) { + CloseHandle(job); + job = nullptr; + } + + ResumeThread(process.hThread); + } + + if (stdin_write != INVALID_HANDLE_VALUE) { + DWORD written = 0; + WriteFile( + stdin_write, + stdin_content.data(), + static_cast(stdin_content.size()), + &written, + nullptr); + CloseHandle(stdin_write); + } + + const auto started = std::chrono::steady_clock::now(); + std::string collected; + char buffer[4096]; + + while (true) { + DWORD available = 0; + + if (PeekNamedPipe(read_pipe, nullptr, 0, nullptr, &available, nullptr) && available > 0) { + DWORD got = 0; + const DWORD want = available > sizeof(buffer) ? static_cast(sizeof(buffer)) : available; + + if (ReadFile(read_pipe, buffer, want, &got, nullptr) && got > 0) { + collected.append(buffer, static_cast(got)); + continue; + } + } + + const DWORD wait = WaitForSingleObject(process.hProcess, 15); + + if (wait == WAIT_OBJECT_0) { + DWORD leftover = 0; + + while (PeekNamedPipe(read_pipe, nullptr, 0, nullptr, &leftover, nullptr) && leftover > 0) { + DWORD got = 0; + const DWORD want = leftover > sizeof(buffer) ? static_cast(sizeof(buffer)) : leftover; + + if (!ReadFile(read_pipe, buffer, want, &got, nullptr) || got == 0) { + break; + } + + collected.append(buffer, static_cast(got)); + } + + break; + } + + if (timeout_ms > 0) { + const auto elapsed = std::chrono::duration_cast( + std::chrono::steady_clock::now() - started).count(); + + if (elapsed >= static_cast(timeout_ms)) { + result.timed_out = true; + + if (job != nullptr) { + TerminateJobObject(job, 1); + } + else { + TerminateProcess(process.hProcess, 1); + } + + WaitForSingleObject(process.hProcess, INFINITE); + break; + } + } + } + + DWORD code = 1; + GetExitCodeProcess(process.hProcess, &code); + + if (job != nullptr) { + CloseHandle(job); + } + + CloseHandle(process.hProcess); + CloseHandle(process.hThread); + CloseHandle(read_pipe); + + result.output = std::move(collected); + result.output.erase(std::remove(result.output.begin(), result.output.end(), '\r'), result.output.end()); + result.exit_code = result.timed_out ? 1 : static_cast(code); + return result; +#else + int pipe_ends[2] = { -1, -1 }; + + if (pipe(pipe_ends) != 0) { + result.exit_code = 127; + result.output = "could not open a pipe to capture the child's output.\n"; + return result; + } + + int stdin_pipe[2] = { -1, -1 }; + + if (!stdin_content.empty() && pipe(stdin_pipe) != 0) { + close(pipe_ends[0]); + close(pipe_ends[1]); + result.exit_code = 127; + result.output = "could not open a pipe to feed the child's input.\n"; + return result; + } + + std::fflush(nullptr); + const pid_t child = fork(); + + if (child < 0) { + close(pipe_ends[0]); + close(pipe_ends[1]); + + if (stdin_pipe[0] != -1) { + close(stdin_pipe[0]); + close(stdin_pipe[1]); + } + + result.exit_code = 127; + result.output = "could not fork.\n"; + return result; + } + + if (child == 0) { + setpgid(0, 0); + close(pipe_ends[0]); + dup2(pipe_ends[1], STDOUT_FILENO); + dup2(pipe_ends[1], STDERR_FILENO); + close(pipe_ends[1]); + + if (stdin_pipe[0] != -1) { + dup2(stdin_pipe[0], STDIN_FILENO); + close(stdin_pipe[0]); + close(stdin_pipe[1]); + } + + if (!working_directory.empty()) { + if (chdir(working_directory.string().c_str()) != 0) { + _exit(127); + } + } + + for (const auto &pair : extra_env) { + setenv(pair.first.c_str(), pair.second.c_str(), 1); + } + + std::vector child_argv; + child_argv.reserve(resolved.size() + 1); + + for (std::string &arg : resolved) { + child_argv.push_back(arg.data()); + } + + child_argv.push_back(nullptr); + execv(program.c_str(), child_argv.data()); + _exit(127); + } + + setpgid(child, child); + close(pipe_ends[1]); + + if (stdin_pipe[0] != -1) { + close(stdin_pipe[0]); + const ssize_t wrote = write(stdin_pipe[1], stdin_content.data(), stdin_content.size()); + (void)wrote; + close(stdin_pipe[1]); + } + + bool timed_out = false; + result.output = drain_fd(pipe_ends[0], timeout_ms, timed_out); + close(pipe_ends[0]); + + if (timed_out) { + if (kill(-child, SIGKILL) != 0) { + kill(child, SIGKILL); + } + + result.timed_out = true; + } + + int status = 0; + + while (waitpid(child, &status, 0) < 0) { + if (errno != EINTR) { + result.exit_code = 127; + return result; + } + } + + if (timed_out) { + result.exit_code = 128 + SIGKILL; + result.signal = SIGKILL; + return result; + } + + if (WIFSIGNALED(status)) { + result.signal = WTERMSIG(status); + result.exit_code = 128 + result.signal; + return result; + } + + result.exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : 127; + result.output.erase(std::remove(result.output.begin(), result.output.end(), '\r'), result.output.end()); + return result; +#endif +} + +Compiler::CapturedProcess Compiler::run_shell(const std::string &command, unsigned timeout_ms) +{ +#if defined(_WIN32) + return run_captured({ "cmd.exe", "/c", command }, timeout_ms); +#else + return run_captured({ "/bin/sh", "-c", command }, timeout_ms); +#endif } diff --git a/src/Compiler/LLVM/Codegen/AbortCodegen.cpp b/src/Compiler/LLVM/Codegen/AbortCodegen.cpp index 8127b878..65c7eef4 100644 --- a/src/Compiler/LLVM/Codegen/AbortCodegen.cpp +++ b/src/Compiler/LLVM/Codegen/AbortCodegen.cpp @@ -349,14 +349,9 @@ void AbortCodegen::flush_stdout() void AbortCodegen::write_stderr(llvm::Value *ptr, llvm::Value *len) { // write(2, ...) rather than fprintf(stderr, ...): the `stderr` global is spelled differently - // per platform (`__stderrp` on Darwin) and is not portably addressable from IR - llvm::Type *i32 = llvm::Type::getInt32Ty(*_ctx.llvm_context); - llvm::Type *i64 = llvm::Type::getInt64Ty(*_ctx.llvm_context); - llvm::Type *opaque_ptr = _ctx.opaque_ptr_type(); - - _ctx.builder->CreateCall( - _ctx.libc_callee("write", i64, { i32, opaque_ptr, i64 }), - { llvm::ConstantInt::get(i32, 2), ptr, len }); + // per platform (`__stderrp` on Darwin) and is not portably addressable from IR. Windows + // has no `write`; CodegenContext::emit_libc_write names `_write` there + _ctx.emit_libc_write(2, ptr, len); } void AbortCodegen::call_thunk( diff --git a/src/Compiler/LLVM/Codegen/Backend.cpp b/src/Compiler/LLVM/Codegen/Backend.cpp index b5bf0703..65b600bd 100644 --- a/src/Compiler/LLVM/Codegen/Backend.cpp +++ b/src/Compiler/LLVM/Codegen/Backend.cpp @@ -7,6 +7,7 @@ #include "Compiler/TargetSubtarget.h" #include +#include #include #include #include @@ -19,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -26,6 +29,18 @@ #include #include +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include +#include +#include +#include +#include +#include +#endif + #include #include @@ -113,6 +128,89 @@ bool Backend::prepare_execution() const Compiler::Subtarget sub = subtarget(); const std::vector attributes = split_target_features(sub.features); +#if defined(_WIN32) + // MCJIT searches loaded modules for externals. msvcrt is always loaded on + // Windows and exports printf/fflush/_write, so the JIT would bind those + // independently of the UCRT echoc itself uses. echo of a string then + // `_write`s through one CRT while echo of an int `printf`s through the + // other, and the two buffers flush in the wrong order. AddSymbol is + // consulted first, so these pins are the process's own UCRT. + const auto pin = [](const char *name, auto *fn) { + llvm::sys::DynamicLibrary::AddSymbol( + name, reinterpret_cast(reinterpret_cast(fn))); + }; + + pin("printf", printf); + pin("fflush", fflush); + pin("setvbuf", setvbuf); + pin("snprintf", snprintf); + pin("_write", _write); + pin("_get_osfhandle", _get_osfhandle); + pin("_read", _read); + pin("_open", _open); + pin("_close", _close); + pin("_lseeki64", _lseeki64); + pin("_unlink", _unlink); + pin("_access", _access); + pin("rename", rename); + pin("_errno", _errno); + pin("strerror", strerror); + pin("strtod", strtod); + pin("GetCurrentThreadId", GetCurrentThreadId); + pin("SwitchToThread", SwitchToThread); + pin("exit", exit); + pin("malloc", malloc); + pin("free", free); + pin("realloc", realloc); + pin("getenv", getenv); + pin("strcmp", strcmp); + pin("strlen", strlen); + pin("memcpy", memcpy); + pin("memmove", memmove); + pin("memset", memset); + + const auto pin_libm = [&](const char *name, const char *imp_name, auto fn, void *slot) { + pin(name, fn); + pin(imp_name, slot); + }; + + static double (*imp_sin)(double) = static_cast(::sin); + static double (*imp_cos)(double) = static_cast(::cos); + static double (*imp_tan)(double) = static_cast(::tan); + static double (*imp_tanh)(double) = static_cast(::tanh); + static double (*imp_atan2)(double, double) = static_cast(::atan2); + static double (*imp_pow)(double, double) = static_cast(::pow); + static double (*imp_exp2)(double) = static_cast(::exp2); + static double (*imp_log2)(double) = static_cast(::log2); + static double (*imp_log10)(double) = static_cast(::log10); + static double (*imp_floor)(double) = static_cast(::floor); + static double (*imp_ceil)(double) = static_cast(::ceil); + static double (*imp_round)(double) = static_cast(::round); + static double (*imp_trunc)(double) = static_cast(::trunc); + static double (*imp_copysign)(double, double) = static_cast(::copysign); + static double (*imp_fma)(double, double, double) = + static_cast(::fma); + static double (*imp_fabs)(double) = static_cast(::fabs); + + pin_libm("sin", "__imp_sin", imp_sin, &imp_sin); + pin_libm("cos", "__imp_cos", imp_cos, &imp_cos); + pin_libm("tan", "__imp_tan", imp_tan, &imp_tan); + pin_libm("tanh", "__imp_tanh", imp_tanh, &imp_tanh); + pin_libm("atan2", "__imp_atan2", imp_atan2, &imp_atan2); + pin_libm("pow", "__imp_pow", imp_pow, &imp_pow); + pin_libm("exp2", "__imp_exp2", imp_exp2, &imp_exp2); + pin_libm("log2", "__imp_log2", imp_log2, &imp_log2); + pin_libm("log10", "__imp_log10", imp_log10, &imp_log10); + pin_libm("floor", "__imp_floor", imp_floor, &imp_floor); + pin_libm("ceil", "__imp_ceil", imp_ceil, &imp_ceil); + pin_libm("round", "__imp_round", imp_round, &imp_round); + pin_libm("trunc", "__imp_trunc", imp_trunc, &imp_trunc); + pin_libm("copysign", "__imp_copysign", imp_copysign, &imp_copysign); + pin_libm("fma", "__imp_fma", imp_fma, &imp_fma); + pin_libm("fabs", "__imp_fabs", imp_fabs, &imp_fabs); + pin("__acrt_iob_func", __acrt_iob_func); +#endif + std::string errorStr; const llvm::TargetOptions opts; _engine = llvm::EngineBuilder(std::move(_ctx.main_cmp_unit()->llvm_module)) @@ -219,8 +317,16 @@ void Backend::init_target() _ctx.options.no_optimize ? llvm::CodeGenOptLevel::None : llvm::CodeGenOptLevel::Default; llvm::TargetOptions opt; + // LLVM 21 made Triple's string constructor explicit and createTargetMachine + // takes the Triple itself. CI still builds against 20, which takes the string. +#if LLVM_VERSION_MAJOR >= 21 + _target_machine.reset(target->createTargetMachine( + llvm::Triple(_ctx.target_triple), sub.cpu, sub.features, opt, llvm::Reloc::PIC_, + std::nullopt, opt_level)); +#else _target_machine.reset(target->createTargetMachine( _ctx.target_triple, sub.cpu, sub.features, opt, llvm::Reloc::PIC_, std::nullopt, opt_level)); +#endif if (!_target_machine) { throw Compiler::InternalCompilerException(fmt::format( "Could not create a target machine for '{}'", _ctx.target_triple)); @@ -322,6 +428,75 @@ std::optional> host_linker_command( command.insert(command.end(), link_words.begin(), link_words.end()); + return command; +#elif defined(_WIN32) + // lld-link is the Windows spelling of the Apple `ld` path: a linker we + // can invoke without the clang driver. It cannot find the CRT on its + // own, so we only take this path when a bundled sysroot is sitting + // next to echoc or the environment already has LIB (vcvars / CI). + // Otherwise the clang driver is the one that knows how to find VS. + const std::filesystem::path sysroot = Compiler::windows_sysroot(); + const char *libenv = std::getenv("LIB"); + if (sysroot.empty() && (libenv == nullptr || *libenv == '\0')) { + return std::nullopt; + } + + std::vector command = { + "lld-link", + "/nologo", + "/subsystem:console", + "/out:" + executable_name, + }; + + if (!sysroot.empty()) { + command.push_back("/libpath:" + (sysroot / "lib").string()); + } + + // Echo objects have no /DEFAULTLIB comments. These are the CRT and + // Win32 libs a console program needs; lld-link finds them on LIB or + // the sysroot libpath above. + command.push_back("/defaultlib:libcmt"); + command.push_back("/defaultlib:libucrt"); + command.push_back("/defaultlib:libvcruntime"); + command.push_back("/defaultlib:oldnames"); + command.push_back("/defaultlib:kernel32"); + command.push_back("/defaultlib:user32"); + command.push_back("/defaultlib:advapi32"); + command.push_back("/defaultlib:shell32"); + command.push_back("/defaultlib:ws2_32"); + + append_objects(command, objects); + append_objects(command, link_objects); + + for (size_t i = 0; i < link_words.size(); i++) { + const std::string &word = link_words[i]; + + if (word == "-framework") { + if (i + 1 < link_words.size()) { + i++; + } + continue; + } + + if (word.rfind("-L", 0) == 0) { + command.push_back("/libpath:" + word.substr(2)); + continue; + } + + if (word.rfind("-l", 0) == 0) { + const std::string name = word.substr(2); + // libm is a Unix split. UCRT already has the math symbols, and + // lld-link looking for m.lib is a missing-library error + if (name == "m") { + continue; + } + command.push_back(name + ".lib"); + continue; + } + + command.push_back(word); + } + return command; #else (void)executable_name; @@ -334,6 +509,26 @@ std::optional> host_linker_command( }; +// COFF does not merge `linkonce_odr` unless the symbol is in a COMDAT. +// Without one, two units that both emit `__eco_argc` (or `__eco_abort`, ...) +// fail at link with a duplicate symbol. Mach-O rejects COMDATs outright +// (`LLVM ERROR: MachO doesn't support COMDATs`), so this is Windows only. +// ELF would accept a group, but does not need one. +static void assign_odr_comdats(llvm::Module &module) +{ + for (llvm::GlobalVariable &global : module.globals()) { + if (global.hasLinkOnceODRLinkage() && !global.hasComdat()) { + global.setComdat(module.getOrInsertComdat(global.getName())); + } + } + + for (llvm::Function &fn : module) { + if (fn.hasLinkOnceODRLinkage() && !fn.hasComdat()) { + fn.setComdat(module.getOrInsertComdat(fn.getName())); + } + } +} + bool Backend::emit_object(Compiler::LLVM::CmpUnit &cmp_unit, const std::filesystem::path &object_path) { // the same TargetMachine codegen took its data layout from (Backend::init_target), so the @@ -349,6 +544,10 @@ bool Backend::emit_object(Compiler::LLVM::CmpUnit &cmp_unit, const std::filesyst return false; } + if (_ctx.targeting_windows()) { + assign_odr_comdats(*cmp_unit.llvm_module); + } + // the directory is the driver's - it prepared one before it decided to emit here at all. Creating one // on the way past would be a second answer to where a build artifact goes, in the layer furthest from // the question @@ -392,9 +591,17 @@ bool Backend::link_executable( std::vector link_words; Compiler::partition_link_requirements(link, link_objects, link_words); - if (const auto command = host_linker_command(executable_name, objects, link_objects, link_words)) { + std::string output = executable_name; + +#if defined(_WIN32) + if (std::filesystem::path(output).extension().empty()) { + output += ".exe"; + } +#endif + + if (const auto command = host_linker_command(output, objects, link_objects, link_words)) { if (Compiler::run_tool(command.value())) { - gen_debug_symbols(executable_name); + gen_debug_symbols(output); return true; } @@ -403,10 +610,20 @@ bool Backend::link_executable( llvm::errs() << "Note: the system linker failed, retrying through the clang driver\n"; } - std::vector fallback = { "clang", "-o", executable_name }; + std::vector fallback = { "clang", "-o", output }; + Compiler::append_windows_sysroot_link_args(fallback); append_objects(fallback, objects); append_objects(fallback, link_objects); - fallback.insert(fallback.end(), link_words.begin(), link_words.end()); + for (const std::string &word : link_words) { +#if defined(_WIN32) + // same skip host_linker_command makes: UCRT already has libm, and + // clang turning `-lm` into m.lib is a missing-library error + if (word == "-lm") { + continue; + } +#endif + fallback.push_back(word); + } // **no message of its own.** Whichever tool ran has already said what went wrong, on the stderr it // inherited; naming which module asked for each requirement is the driver's to render, because that @@ -415,7 +632,7 @@ bool Backend::link_executable( return false; } - gen_debug_symbols(executable_name); + gen_debug_symbols(output); return true; } diff --git a/src/Compiler/LLVM/Codegen/DebugInfoCodegen.cpp b/src/Compiler/LLVM/Codegen/DebugInfoCodegen.cpp index 0f54e5aa..d2adf76f 100644 --- a/src/Compiler/LLVM/Codegen/DebugInfoCodegen.cpp +++ b/src/Compiler/LLVM/Codegen/DebugInfoCodegen.cpp @@ -75,6 +75,24 @@ namespace { return builder.createPointerType(nullptr, ECO_TARGET_POINTER_SIZE * 8); } + + // `DIType::getAlignInBits` is not safe on every derived type we mint (a class + // handle is a pointer whose align field is left 0, and reading it has been an + // access violation). Size is always populated; pick a power-of-two from that. + uint32_t member_align_bits(llvm::DIType *type) + { + const uint64_t size = type->getSizeInBits(); + if (size >= 64) { + return 64; + } + if (size >= 32) { + return 32; + } + if (size >= 16) { + return 16; + } + return 8; + } }; bool DebugInfoCodegen::enabled() const @@ -354,7 +372,7 @@ void DebugInfoCodegen::set_location(const AST::Node &node) // a token AST::Module::make_virtual_token minted needs no arm here: it carries the real line and // column of the site it was minted at, and its *file* - the one thing it cannot answer - is not // part of a DILocation, which takes that from the scope - if (token == nullptr) { + if (token == nullptr || current_scope() == nullptr) { return; } @@ -411,7 +429,8 @@ void DebugInfoCodegen::declare_local( { UnitDebug *unit = unit_debug(); - if (unit == nullptr || _subprogram == nullptr || alloca == nullptr || !decl.has_type()) { + if (unit == nullptr || _subprogram == nullptr || current_scope() == nullptr + || alloca == nullptr || !decl.has_type()) { return; } @@ -482,7 +501,7 @@ llvm::DIType *DebugInfoCodegen::tuple_type_of( /*File=*/nullptr, /*LineNumber=*/0, member_type->getSizeInBits(), - member_type->getAlignInBits(), + member_align_bits(member_type), layout->getElementOffsetInBits(i), llvm::DINode::FlagZero, member_type)); @@ -592,10 +611,32 @@ void DebugInfoCodegen::append_property_members( const Structure &structure = cmp_unit.structure_table->get_structure(struct_id); const size_t count = complex->property_count(); - assert(structure.property_byte_offset.size() == count); + if (structure.property_byte_offset.size() != count) { + return; + } for (size_t i = 0; i < count; i++) { const AST::ComplexType::Property &property = complex->get_property(i); + const uint64_t offset = base_offset_bits + structure.property_byte_offset[i] * 8; + + // a class value is a handle. describing that derived type as a member has been + // an access violation (the box pointer's align/size accessors, and uniquing + // against a replaceable composite). the slot is pointer-sized either way + if (property.type.is_class()) { + llvm::DIType *field_type = opaque_pointer(*unit.builder); + elements.push_back(unit.builder->createMemberType( + unit.cu, + property.name, + decl_file, + decl_line, + ECO_TARGET_POINTER_SIZE * 8, + ECO_TARGET_POINTER_SIZE * 8, + offset, + property.is_private() ? llvm::DINode::FlagPrivate : llvm::DINode::FlagZero, + field_type)); + continue; + } + llvm::DIType *member_type = type_of(property.type, cmp_unit); if (member_type == nullptr) { @@ -608,8 +649,8 @@ void DebugInfoCodegen::append_property_members( decl_file, decl_line, member_type->getSizeInBits(), - member_type->getAlignInBits(), - base_offset_bits + structure.property_byte_offset[i] * 8, + member_align_bits(member_type), + offset, property.is_private() ? llvm::DINode::FlagPrivate : llvm::DINode::FlagZero, member_type)); } @@ -638,15 +679,11 @@ llvm::DIType *DebugInfoCodegen::class_type_of(const AST::ValueType &type, CmpUni const unsigned decl_line = site.has_value() ? site->line : 0; // a class *value* is a handle into the heap block, so what a debugger must be told is "pointer to - // the box". Interned as a placeholder first for struct_type_of's reason - a class naming itself is - // the ordinary case rather than the exotic one - llvm::DICompositeType *placeholder = unit->builder->createReplaceableCompositeType( - llvm::dwarf::DW_TAG_structure_type, name, unit->cu, decl_file, decl_line); - - llvm::DIType *box_placeholder_ptr = unit->builder->createPointerType( - placeholder, ECO_TARGET_POINTER_SIZE * 8); - - unit->types[type] = box_placeholder_ptr; + // the box". Intern an opaque pointer first rather than a pointer to a replaceable composite: + // `replaceTemporary` does not reliably RAUW `DIDerivedType` bases, and a later `getAlignInBits` + // on that dangling derived type is an access violation. An opaque stand-in is what a recursive + // field sees during construction; the cache is overwritten with the real handle below + unit->types[type] = opaque_pointer(*unit->builder); const llvm::StructLayout *box_layout = _ctx.layout().getStructLayout(class_layout.box); std::vector elements; @@ -666,7 +703,7 @@ llvm::DIType *DebugInfoCodegen::class_type_of(const AST::ValueType &type, CmpUni for (size_t i = 0; i < header.size(); i++) { elements.push_back(unit->builder->createMemberType( unit->cu, header[i].first, decl_file, decl_line, - header[i].second->getSizeInBits(), header[i].second->getAlignInBits(), + header[i].second->getSizeInBits(), 64, box_layout->getElementOffsetInBits(i), llvm::DINode::FlagArtificial, header[i].second)); } @@ -701,8 +738,6 @@ llvm::DIType *DebugInfoCodegen::class_type_of(const AST::ValueType &type, CmpUni /*VTableHolder=*/nullptr, complex->mangled_token() + ".box"); - unit->builder->replaceTemporary(llvm::TempDIType(placeholder), box); - llvm::DIType *handle = unit->builder->createPointerType(box, ECO_TARGET_POINTER_SIZE * 8); unit->types[type] = handle; diff --git a/src/Compiler/LLVM/Codegen/ExprCodegen.cpp b/src/Compiler/LLVM/Codegen/ExprCodegen.cpp index b59c98a3..4f33b49d 100644 --- a/src/Compiler/LLVM/Codegen/ExprCodegen.cpp +++ b/src/Compiler/LLVM/Codegen/ExprCodegen.cpp @@ -1125,17 +1125,13 @@ void ExprCodegen::gen_echo_string(llvm::Value *value, const AST::ValueType &type _ctx.libc_callee("fflush", i32, { _ctx.opaque_ptr_type() }), { llvm::ConstantPointerNull::get(llvm::cast(_ctx.opaque_ptr_type())) }); - _ctx.builder->CreateCall( - _ctx.libc_callee("write", i64, { i32, _ctx.opaque_ptr_type(), i64 }), - { llvm::ConstantInt::get(i32, 1), bytes, size }); + _ctx.emit_libc_write(1, bytes, size); // the trailing newline every other `echo` conversion carries, so one statement behaves one way // whatever it is handed. writing it separately rather than copying the bytes to append it: the // bytes may be a shared substring, and there is nowhere to put a longer copy - _ctx.builder->CreateCall( - _ctx.libc_callee("write", i64, { i32, _ctx.opaque_ptr_type(), i64 }), - { llvm::ConstantInt::get(i32, 1), _ctx.builder->CreateGlobalStringPtr("\n", "echo.nl"), - llvm::ConstantInt::get(i64, 1) }); + _ctx.emit_libc_write( + 1, _ctx.builder->CreateGlobalStringPtr("\n", "echo.nl"), llvm::ConstantInt::get(i64, 1)); } void ExprCodegen::gen_ref_count_builtin(AST::FunctionCallExprNode &node, AST::BuiltinKind kind) diff --git a/src/Compiler/LLVM/Codegen/ProcessCodegen.cpp b/src/Compiler/LLVM/Codegen/ProcessCodegen.cpp index bcc6dfd9..ff37b6c8 100644 --- a/src/Compiler/LLVM/Codegen/ProcessCodegen.cpp +++ b/src/Compiler/LLVM/Codegen/ProcessCodegen.cpp @@ -47,6 +47,8 @@ void ProcessCodegen::gen_capture(llvm::Function *entry) _ctx.builder->CreateStore(entry->getArg(1), get_or_create_argv()); _ctx.builder->CreateStore(entry->getArg(2), get_or_create_envp()); + + _ctx.emit_unbuffer_stdio(); } llvm::Value *ProcessCodegen::gen_argc(const llvm::Twine &name) diff --git a/src/Compiler/LLVM/Codegen/StaticStorageCodegen.cpp b/src/Compiler/LLVM/Codegen/StaticStorageCodegen.cpp index 929e3760..a4597bad 100644 --- a/src/Compiler/LLVM/Codegen/StaticStorageCodegen.cpp +++ b/src/Compiler/LLVM/Codegen/StaticStorageCodegen.cpp @@ -116,9 +116,20 @@ namespace Compiler::LLVM // i64, not ptr: stdlib declares `pthread_self` as returning usize, and two units that // disagree about that symbol's type emit two __eco_static_once bodies the ODR check // refuses. the bits in the return register are the thread id either way - llvm::Value *self = _ctx.builder->CreateCall( - _ctx.libc_callee("pthread_self", i64, {}), {}, "self"); - _ctx.needs_pthread = true; + llvm::Type *i32 = llvm::Type::getInt32Ty(*_ctx.llvm_context); + llvm::Value *self = nullptr; + + if (_ctx.targeting_windows()) { + // DWORD GetCurrentThreadId(void) - kernel32, already on the link line + llvm::Value *tid = _ctx.builder->CreateCall( + _ctx.libc_callee("GetCurrentThreadId", i32, {}), {}, "tid"); + self = _ctx.builder->CreateZExt(tid, i64, "self"); + } + else { + self = _ctx.builder->CreateCall( + _ctx.libc_callee("pthread_self", i64, {}), {}, "self"); + _ctx.needs_pthread = true; + } // 0 is uninitialized and ~0 is done. a tid that collides with either would skip // the initializer or look finished. 1 is neither sentinel llvm::Value *one = llvm::ConstantInt::get(i64, 1); @@ -166,9 +177,12 @@ namespace Compiler::LLVM _ctx.builder->CreateBr(done); _ctx.builder->SetInsertPoint(wait); - _ctx.builder->CreateCall( - _ctx.libc_callee( - "sched_yield", llvm::Type::getInt32Ty(*_ctx.llvm_context), {})); + if (_ctx.targeting_windows()) { + _ctx.builder->CreateCall(_ctx.libc_callee("SwitchToThread", i32, {})); + } + else { + _ctx.builder->CreateCall(_ctx.libc_callee("sched_yield", i32, {})); + } _ctx.builder->CreateBr(loop); _ctx.builder->SetInsertPoint(done); diff --git a/src/Compiler/LLVM/Codegen/TypeLowering.cpp b/src/Compiler/LLVM/Codegen/TypeLowering.cpp index 981472a7..fdf7538b 100644 --- a/src/Compiler/LLVM/Codegen/TypeLowering.cpp +++ b/src/Compiler/LLVM/Codegen/TypeLowering.cpp @@ -24,10 +24,12 @@ #include "AST/FunctionDeclNode.h" #include "AST/TypeDeclNode.h" +#include #include #include #include #include +#include #include @@ -66,7 +68,11 @@ void TypeLowering::create_cmp_units( // question about type sizes during codegen gets the real target's answer, the optimizer // has a layout to reason with, and the JIT and object paths cannot disagree cmp_unit->llvm_module->setDataLayout(_ctx.layout()); +#if LLVM_VERSION_MAJOR >= 21 + cmp_unit->llvm_module->setTargetTriple(llvm::Triple(_ctx.target_triple)); +#else cmp_unit->llvm_module->setTargetTriple(_ctx.target_triple); +#endif // and its compile unit and debug module flags, for the same reason and at the same moment: all // three are things a module carries from the instant it exists rather than acquires later. a diff --git a/src/Compiler/LLVM/LLVMCompiler.cpp b/src/Compiler/LLVM/LLVMCompiler.cpp index 10088cc2..e12b414a 100644 --- a/src/Compiler/LLVM/LLVMCompiler.cpp +++ b/src/Compiler/LLVM/LLVMCompiler.cpp @@ -2,6 +2,7 @@ #include "Compiler/LLVM/OdrComparison.h" #include "Compiler/PhaseTimings.h" +#include "Compiler/TestRunner.h" #include "AST/ASTFunctionEmission.h" #include "AST/FunctionDeclNode.h" @@ -10,7 +11,9 @@ #include "AST/ConstIfNode.h" #include "AST/ConstExprNode.h" +#include #include +#include #include #include #include @@ -61,6 +64,84 @@ void LLVMCompiler::set_test_mode(bool test_mode) _ctx.test_mode = test_mode; } +void LLVMCompiler::set_native_test_runner(bool enabled) +{ + _ctx.emit_native_test_runner = enabled; +} + +void LLVMCompiler::set_test_symbols(std::vector symbols) +{ + _ctx.test_symbols = std::move(symbols); +} + +void LLVMCompiler::emit_test_dispatch() +{ + if (!_ctx.test_mode || !_ctx.emit_native_test_runner || _ctx.test_symbols.empty()) { + return; + } + + llvm::Module *module = _ctx.current_module(); + llvm::Function *main_fn = _ctx.builder->GetInsertBlock()->getParent(); + llvm::Type *i32 = _ctx.builder->getInt32Ty(); + llvm::Type *void_ty = _ctx.builder->getVoidTy(); + llvm::PointerType *ptr = llvm::cast(_ctx.opaque_ptr_type()); + + llvm::FunctionCallee getenv_fn = _ctx.libc_callee("getenv", ptr, { ptr }); + llvm::FunctionCallee strcmp_fn = _ctx.libc_callee("strcmp", i32, { ptr, ptr }); + llvm::FunctionCallee exit_fn = _ctx.libc_callee( + "exit", void_ty, { i32 }); + + if (auto *exit_decl = llvm::dyn_cast(exit_fn.getCallee())) { + exit_decl->addFnAttr(llvm::Attribute::NoReturn); + } + + llvm::BasicBlock *after = llvm::BasicBlock::Create(*_ctx.llvm_context, "test.after", main_fn); + llvm::BasicBlock *search = llvm::BasicBlock::Create(*_ctx.llvm_context, "test.search", main_fn); + llvm::BasicBlock *unmatched = llvm::BasicBlock::Create(*_ctx.llvm_context, "test.unmatched", main_fn); + + llvm::Value *name = _ctx.builder->CreateCall( + getenv_fn, + { _ctx.builder->CreateGlobalStringPtr(Compiler::k_isolated_test_env, "eco.test.env") }, + "test.name"); + llvm::Value *missing = _ctx.builder->CreateICmpEQ( + name, llvm::ConstantPointerNull::get(ptr), "test.missing"); + _ctx.builder->CreateCondBr(missing, after, search); + + _ctx.builder->SetInsertPoint(search); + + llvm::FunctionType *test_ty = llvm::FunctionType::get(void_ty, false); + + for (const std::string &symbol : _ctx.test_symbols) { + llvm::BasicBlock *run = llvm::BasicBlock::Create(*_ctx.llvm_context, "test.run", main_fn); + llvm::BasicBlock *cont = llvm::BasicBlock::Create(*_ctx.llvm_context, "test.next", main_fn); + + llvm::Value *want = _ctx.builder->CreateGlobalStringPtr(symbol, "eco.test.sym"); + llvm::Value *cmp = _ctx.builder->CreateCall(strcmp_fn, { name, want }, "test.cmp"); + llvm::Value *eq = _ctx.builder->CreateICmpEQ(cmp, llvm::ConstantInt::get(i32, 0), "test.eq"); + _ctx.builder->CreateCondBr(eq, run, cont); + + _ctx.builder->SetInsertPoint(run); + + llvm::Function *fn = module->getFunction(symbol); + + if (fn == nullptr) { + fn = llvm::Function::Create(test_ty, llvm::Function::ExternalLinkage, symbol, module); + } + + _ctx.builder->CreateCall(fn); + _ctx.builder->CreateBr(after); + _ctx.builder->SetInsertPoint(cont); + } + + _ctx.builder->CreateBr(unmatched); + + _ctx.builder->SetInsertPoint(unmatched); + _ctx.builder->CreateCall(exit_fn, { llvm::ConstantInt::get(i32, 127) }); + _ctx.builder->CreateUnreachable(); + + _ctx.builder->SetInsertPoint(after); +} + void LLVMCompiler::emit_entry_file_roots(Compiler::LLVM::CmpUnit &main_cmp_unit) { // **a test run has no program**, so the file roots are not walked at all rather than walked and @@ -281,6 +362,11 @@ void LLVMCompiler::compile_bundle(const AST::Bundle &bundle, const std::set loadables_in_directory( return found; } -// a `lib.a` sitting in a declared search directory, or nullopt. The one lookup the static -// arm of the renderer is allowed - it does not walk the host and it does not invent flags +// a `lib.a` / `lib.lib` sitting in a declared search directory, or nullopt. The one +// lookup the static arm of the renderer is allowed - it does not walk the host and it does not +// invent flags std::optional archive_in_search( const std::string &name, const std::vector &requirements @@ -259,12 +260,20 @@ std::optional archive_in_search( continue; } - const std::filesystem::path archive = - std::filesystem::path(other.value) / ("lib" + name + ".a"); + const std::filesystem::path directory(other.value); + const std::string candidates[] = { + "lib" + name + ".a", + "lib" + name + ".lib", + name + ".lib", + "lib" + name + "_a.lib", + }; std::error_code ec; - if (std::filesystem::is_regular_file(archive, ec)) { - return archive; + for (const std::string &filename : candidates) { + const std::filesystem::path archive = directory / filename; + if (std::filesystem::is_regular_file(archive, ec)) { + return archive; + } } } diff --git a/src/Compiler/TerminalCapabilities.cpp b/src/Compiler/TerminalCapabilities.cpp index 8d65f7fd..4fcf5420 100644 --- a/src/Compiler/TerminalCapabilities.cpp +++ b/src/Compiler/TerminalCapabilities.cpp @@ -6,7 +6,15 @@ #include #if defined(_WIN32) +#include + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include #include +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif #define ECO_ISATTY(fd) _isatty(fd) #define ECO_STDOUT_FD 1 #define ECO_STDERR_FD 2 @@ -107,7 +115,18 @@ namespace } } -#if !defined(_WIN32) +#if defined(_WIN32) + const HANDLE handle = GetStdHandle(fd == ECO_STDERR_FD ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO info; + + if (handle != INVALID_HANDLE_VALUE && handle != nullptr + && GetConsoleScreenBufferInfo(handle, &info)) { + const int cols = info.srWindow.Right - info.srWindow.Left + 1; + if (cols > 0) { + return static_cast(cols); + } + } +#else struct winsize size; if (ioctl(fd, TIOCGWINSZ, &size) == 0 && size.ws_col > 0) { return size.ws_col; @@ -171,6 +190,38 @@ Compiler::TerminalCapabilities Compiler::TerminalCapabilities::plain() return TerminalCapabilities(); } +void Compiler::prepare_terminal() +{ +#if defined(_WIN32) + static std::once_flag once; + std::call_once(once, [] { + bool attached = false; + + for (const HANDLE handle : { + GetStdHandle(STD_OUTPUT_HANDLE), + GetStdHandle(STD_ERROR_HANDLE), + }) { + DWORD mode = 0; + if (handle == nullptr || handle == INVALID_HANDLE_VALUE + || !GetConsoleMode(handle, &mode)) { + continue; + } + + attached = true; + SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } + + // the OEM page is a property of the console, not of a handle: `_write`, iostreams, + // llvm::errs and printf all decode through it. a pipe is not a console, so this does not + // run for a redirect and a golden still byte-compares + if (attached) { + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); + } + }); +#endif +} + namespace { // **the accepted spellings and the message that lists them, out of one table.** the two flag parsers diff --git a/src/Compiler/TestReporter.cpp b/src/Compiler/TestReporter.cpp index ff61bf76..2916c7be 100644 --- a/src/Compiler/TestReporter.cpp +++ b/src/Compiler/TestReporter.cpp @@ -10,9 +10,13 @@ namespace { // what ended a test, said the way a person would. `strsignal` rather than a table of our own: the names - // are the platform's, and a signal this compiler has never heard of still has one + // are the platform's, and a signal this compiler has never heard of still has one. Windows has no + // `strsignal` and no POSIX signals; the number is still the honest report std::string signal_description(int signal) { +#if defined(_WIN32) + return fmt::format("signal {}", signal); +#else const char *described = strsignal(signal); if (described == nullptr) { @@ -20,6 +24,7 @@ namespace } return fmt::format("{} (signal {})", described, signal); +#endif } // the one sentence that says how a test ended, shared by both renderings so a CI log and a terminal diff --git a/src/Compiler/TestRunner.cpp b/src/Compiler/TestRunner.cpp index 0372dd38..bd778042 100644 --- a/src/Compiler/TestRunner.cpp +++ b/src/Compiler/TestRunner.cpp @@ -1,5 +1,6 @@ #include "Compiler/TestRunner.h" +#include "Compiler/HostTool.h" #include "Compiler/ProgressReporter.h" #include @@ -101,9 +102,40 @@ namespace #endif }; -bool Compiler::test_isolation_available() +Compiler::TestResult Compiler::run_test_isolated( + const TestCase &test, + const std::vector &argv, + unsigned timeout_ms +) { - return ECO_TEST_ISOLATION_POSIX != 0; + TestResult result; + result.test = test; + + const auto started = std::chrono::steady_clock::now(); + + const CapturedProcess captured = run_captured( + argv, + timeout_ms, + {}, + { { k_isolated_test_env, test.symbol } }); + + result.milliseconds = progress_elapsed_ms(started); + result.output = captured.output; + result.status = captured.exit_code; + result.signal = captured.signal; + + if (captured.timed_out) { + result.outcome = TestOutcome::t_timed_out; + return result; + } + + if (captured.signal != 0) { + result.outcome = TestOutcome::t_signalled; + return result; + } + + result.outcome = result.status == 0 ? TestOutcome::t_passed : TestOutcome::t_failed; + return result; } #if ECO_TEST_ISOLATION_POSIX @@ -220,25 +252,4 @@ Compiler::TestResult Compiler::run_test_isolated( return result; } -#else - -Compiler::TestResult Compiler::run_test_isolated( - const TestCase &test, - const std::function &call, - unsigned timeout_ms -) -{ - (void)call; - (void)timeout_ms; - - // unreachable: the subcommand refuses before the first test when test_isolation_available() is false, - // which is where the refusal belongs - a platform that cannot isolate is told so once - TestResult result; - result.test = test; - result.outcome = TestOutcome::t_failed; - result.output = "running a test in its own process is not implemented on this platform.\n"; - - return result; -} - #endif diff --git a/src/main.cpp b/src/main.cpp index b6f21246..47422376 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,11 +29,13 @@ #include "Compiler/CommandLineOption.h" #include "Compiler/DriverOptions.h" #include "Compiler/CompilerException.h" +#include "Compiler/HostTool.h" #include "Compiler/LinkRequirement.h" #include "Compiler/ModuleCache.h" #include "Compiler/PhaseTimings.h" #include "Compiler/ProgressReporter.h" #include "Compiler/SettledPath.h" +#include "Compiler/TargetFacts.h" #include "Compiler/TargetSubtarget.h" #include "Compiler/TestReporter.h" #include "Compiler/TestRunner.h" @@ -47,12 +49,19 @@ #include "stdlib_embedded.h" #endif +#if defined(_WIN32) +#include +#include +#else #include +#endif #include +#include +#include #include +#include #include -#include // the files an argument names, expanding wildcards. `missing` counts the paths that were named // literally and are not there - a caller has to refuse those rather than compile what is left, since @@ -1093,7 +1102,19 @@ static std::vector compiled_manifests( // two builds running at once do not share a directory one of them is going to remove static std::filesystem::path fallback_scratch_dir() { - return std::filesystem::temp_directory_path() / "echoc" / std::to_string(getpid()); +#if defined(_WIN32) + const int pid = _getpid(); + + // not `%TEMP%`: Defender treats a newly-linked unsigned exe there as a dropper + // and blocks the thread. stdlib objects already live under `%LOCALAPPDATA%\echo` + if (const char *local = std::getenv("LOCALAPPDATA"); local != nullptr && *local != '\0') { + return std::filesystem::path(local) / "echo" / "scratch" / std::to_string(pid); + } +#else + const int pid = getpid(); +#endif + + return std::filesystem::temp_directory_path() / "echoc" / std::to_string(pid); } // keying a module reads every one of its sources, so it is not free. Only two things ever look at a key: the @@ -1381,7 +1402,8 @@ static bool resolve_programs( /*name=*/{}, entry == nullptr ? ECO_MAIN_MODULE_NAME : entry->name, /*entry_file=*/{}, - driver.output + driver.output, + /*active_targets=*/{} }); return true; @@ -1416,7 +1438,8 @@ static bool resolve_programs( target.name, entry->name, target.entry, - driver.output.empty() ? out.layout.target_binary(*entry, target.name) : driver.output + driver.output.empty() ? out.layout.target_binary(*entry, target.name) : driver.output, + /*active_targets=*/{} }; // **only the target being built, and only in the module that declared it.** Two targets of one @@ -1912,9 +1935,103 @@ static void optimize_if_asked(const Compiler::DriverOptions &driver, LLVMCompile step.finish(true); } +// C objects, codegen, optional whole-program merge, emit, link. **one path**, so `build`, a +// Windows `run`, and a linked test runner cannot drift on `--print ir` or `-O`. +// +// nullopt is success; anything else is the exit status the subcommand owes its caller +static std::optional compile_and_link_executable( + const Compiler::DriverOptions &driver, + const AST::DiagnosticRenderer &diagnostics, + FrontEnd &front, + AST::Bundle &bundle, + LLVMCompiler &compiler, + const std::string &output, + const ModulePlan &plan, + bool whole_program +) +{ + std::vector link; + + if (!collect_link_requirements(driver, diagnostics, front, link)) { + return 1; + } + + { + Compiler::ScopedPhase phase("cc"); + CModuleBuilds c_builds; + + if (!build_c_modules(driver, diagnostics, front, /*for_jit=*/false, c_builds)) { + return 1; + } + + std::string link_error; + + if (!Compiler::merge_link_requirements(c_builds.objects, link, link_error)) { + diagnostics.render_untyped("Invalid Link Requirement", link_error); + return 1; + } + } + + try { + Compiler::ScopedPhase phase("codegen"); + Compiler::ProgressStep step( + Compiler::ProgressReporter::instance(), Compiler::ProgressPhase::t_codegen); + + compiler.compile_bundle(bundle, plan.cached); + + if (whole_program) { + compiler.link_into_main(); + } + + step.finish(true); + } catch (Compiler::ASTCompilerException &e) { + return report_compiler_exception(diagnostics, e); + } + + if (!merge_emitted_runtime_link(diagnostics, compiler, link)) { + return 1; + } + + optimize_if_asked(driver, compiler); + + if (driver.prints(Compiler::PrintKind::t_ir)) { + compiler.printIR(false); + } + + if (driver.prints(Compiler::PrintKind::t_ir_units)) { + compiler.print_unit_ir(); + } + + if (front.layout().prepare_scratch_dir() != Compiler::BuildDirState::t_ready) { + diagnostics.render_untyped("Cannot Build Here", fmt::format( + "'{}' could not be created, and the objects on the way to '{}' have nowhere to go.", + front.layout().scratch_dir().string(), output)); + return 1; + } + + { + Compiler::ScopedPhase phase("emit + link"); + Compiler::ProgressStep step( + Compiler::ProgressReporter::instance(), + Compiler::ProgressPhase::t_emit, + std::filesystem::path(output).filename().string()); + + const bool linked = emit_and_link_modules(compiler, front.layout(), output, plan, link); + step.finish(linked); + + if (!linked) { + report_link_failure(diagnostics, link); + return 1; + } + } + + return std::nullopt; +} + // **everything between a parsed bundle and a JIT that can be asked for an address**, shared by the two -// subcommands that run one: the cache plan, the link requirements, the C modules, the native libraries the -// JIT must have open before it resolves a symbol, then codegen and the whole-program optimizer. +// Unix subcommands that run one: the cache plan, the link requirements, the C modules, the native +// libraries the JIT must have open before it resolves a symbol, then codegen and the whole-program +// optimizer. // // `run` and `test` do the same work here and differ only in what they do with the compiler afterwards - one // runs the program, the other calls one definition at a time - so this is one function rather than the two @@ -2043,7 +2160,47 @@ int main_run( const std::string &entry_module = front.entry_module(); LLVMCompiler compiler(front.options); + int status = 0; + +#if defined(_WIN32) + const Compiler::TargetFacts &facts = front.target_facts(); + const Compiler::TargetFacts host = Compiler::TargetFacts::host(); + // a host Windows binary. `--target-os darwin` still has to JIT: native-linking + // Darwin IR asks for pthread.lib that this machine does not have + const bool native_run = + facts.operating_system == host.operating_system + && facts.architecture == host.architecture + && !driver.explains(Compiler::ExplainKind::t_prune) + && !front.layout().scratch_is_temporary(); +#else + const bool native_run = false; +#endif + + if (native_run) { + compiler.set_entry(entry_module, front.entry_file()); + + report_cache_plan( + driver, front.manifests(), front.cache_keys, ModulePlan{}, entry_module, + /*bypassed=*/true); + + const std::filesystem::path runner = front.layout().scratch_dir() / "eco_run.exe"; + if (const std::optional failed = compile_and_link_executable( + driver, diagnostics, front, bundle, compiler, runner.string(), ModulePlan{}, + driver.whole_program)) { + return failed.value(); + } + + std::vector argv = { program_name(driver, program) }; + argv.insert(argv.end(), driver.program_arguments.begin(), driver.program_arguments.end()); + + Compiler::ProgressReporter::instance().close( + fmt::format("compiled '{}'", entry_module), Compiler::progress_elapsed_ms(started)); + + status = Compiler::run_wait(runner.string(), argv); + (void)environment; + } + else { if (const std::optional failed = prepare_jit( driver, diagnostics, front, bundle, /*test_mode=*/false, compiler)) { return failed.value(); @@ -2065,11 +2222,11 @@ int main_run( Compiler::ProgressReporter::instance().close( fmt::format("compiled '{}'", entry_module), Compiler::progress_elapsed_ms(started)); - int status = 0; { Compiler::ScopedPhase phase("jit"); status = compiler.prepare_execution() ? compiler.run_main(argv, environment) : 1; } + } // after the program, because the prune happened inside the run - the same position `[timings]` takes, // and for the same reason @@ -2082,9 +2239,9 @@ int main_run( // after the program, because a C module's loadable library lives there and was open for the whole of it front.layout().discard_temporary_scratch(); - // the program's own status, so `echoc run` exits the way it did. Today that is always 0 on this - // path - the entry point's epilogue returns 0 and every other ending goes through libc's `exit` - // from inside the JIT, which never comes back here at all + // the program's own status, so `echoc run` exits the way it did. a module-scope `die` on the + // JIT path never comes back here (libc `exit` from inside the engine); a spawned runner + // reports the child's code return status; } @@ -2154,26 +2311,15 @@ static bool resolve_test_selection( // **compiles the module and runs its tests, each in a process of its own.** // -// down to the codegen step this is main_run, and it has to be: a test run is the JIT, so it needs the same -// link requirements resolved, the same C modules built and the same native libraries open - which is why -// both go through prepare_jit rather than each spelling it. What differs begins after the compile: there is -// no program to run, only a list of definitions to call one at a time +// Unix shares prepare_jit with `run` so the two cannot drift. Windows (no fork into a JIT) compiles +// a linked runner through compile_and_link_executable - the same function `build` uses - and spawns +// it once per test. Isolation is "one process per test"; fork vs. spawn is TestRunner's question. int main_test( const Compiler::DriverOptions &driver, const AST::DiagnosticRenderer &diagnostics, const Compiler::TerminalCapabilities &capabilities, const char *const *environment) { - // **refused here rather than at the first test**, which is the call `#[link: framework]` makes about - // Darwin: a platform that cannot do the thing is told so where the thing was asked for, and every test - // "failing" for the same reason is not a report anybody can act on - if (!Compiler::test_isolation_available()) { - diagnostics.render_untyped("Tests Cannot Be Isolated", - "'echoc test' runs each test in a process of its own, and this platform has no fork. Every " - "other subcommand is unaffected."); - return 1; - } - const auto started = std::chrono::steady_clock::now(); auto bundle = AST::Bundle(); @@ -2224,6 +2370,36 @@ int main_test( LLVMCompiler compiler(front.options); + std::vector roots; + for (const Compiler::TestCase &test : selected) { + roots.push_back(test.symbol); + } + + compiler.set_test_symbols(roots); + + std::filesystem::path runner; + +#if defined(_WIN32) + (void)environment; + + // no fork into this process's JIT: compile a runner through the same emit+link + // `build` uses, whose `main` dispatches on ECO_INTERNAL_RUN_TEST + compiler.set_entry(front.entry_module(), front.entry_file()); + compiler.set_test_mode(true); + compiler.set_native_test_runner(true); + + report_cache_plan( + driver, front.manifests(), front.cache_keys, ModulePlan{}, front.entry_module(), + /*bypassed=*/true); + + runner = front.layout().scratch_dir() / "eco_tests.exe"; + + if (const std::optional failed = compile_and_link_executable( + driver, diagnostics, front, bundle, compiler, runner.string(), ModulePlan{}, + driver.whole_program)) { + return failed.value(); + } +#else if (const std::optional failed = prepare_jit( driver, diagnostics, front, bundle, /*test_mode=*/true, compiler)) { return failed.value(); @@ -2233,11 +2409,6 @@ int main_test( // them - nothing reaches a test from the entry point, that being the whole of what a test is - and // function_address answers 0 for each. It also means `--filter` narrows the machine code and not only // the run - std::vector roots; - for (const Compiler::TestCase &test : selected) { - roots.push_back(test.symbol); - } - compiler.set_jit_roots(std::move(roots)); if (!compiler.prepare_execution()) { @@ -2257,6 +2428,7 @@ int main_test( return 1; } } +#endif // **the facts main() settled, never a second resolve.** Two places deciding independently whether to // emit an escape sequence is how a redirected stream ends up with half of them in it - and these are @@ -2270,6 +2442,10 @@ int main_test( reporter.begin(selected.size()); for (const Compiler::TestCase &test : selected) { +#if defined(_WIN32) + reporter.result(Compiler::run_test_isolated( + test, { runner.string() }, driver.timeout_ms)); +#else const uint64_t address = compiler.function_address(test.symbol); // **loud rather than a call through null.** A test the prune dropped is a mistake in the root set @@ -2284,6 +2460,7 @@ int main_test( reporter.result(Compiler::run_test_isolated(test, [address]() { reinterpret_cast(address)(); }, driver.timeout_ms)); +#endif } const bool passed = reporter.finish(); @@ -2346,106 +2523,12 @@ static int build_one_program( const std::string output = program.output.string(); - // before codegen, for the reason `run` states: a C source that does not compile fails this build - // whatever the Echo half does, and the wait is the same either way - std::vector link; - - if (!collect_link_requirements(driver, diagnostics, front, link)) { - return 1; - } - - { - Compiler::ScopedPhase phase("cc"); - - CModuleBuilds c_builds; - - if (!build_c_modules( - driver, diagnostics, front, /*for_jit=*/false, c_builds)) { - return 1; - } - - // ahead of every library, which partition_link_requirements is what guarantees - std::string link_error; - - if (!Compiler::merge_link_requirements(c_builds.objects, link, link_error)) { - diagnostics.render_untyped("Invalid Link Requirement", link_error); - return 1; - } - } - LLVMCompiler compiler(options); compiler.set_entry(entry_module, front.entry_file()); - try { - Compiler::ScopedPhase phase("codegen"); - - // inside the try for the reason main_run's is - Compiler::ProgressStep step( - Compiler::ProgressReporter::instance(), Compiler::ProgressPhase::t_codegen); - - compiler.compile_bundle(bundle, plan.cached); - - if (whole_program) { - compiler.link_into_main(); - } - - step.finish(true); - } catch (Compiler::ASTCompilerException &e) { - return report_compiler_exception(diagnostics, e); - } - - if (!merge_emitted_runtime_link(diagnostics, compiler, link)) { - return 1; - } - - optimize_if_asked(driver, compiler); - - if (driver.prints(Compiler::PrintKind::t_ir)) { - compiler.printIR(false); - } - - // after the merge decision above and before the emit below, so what it prints is what gets written. - // it optimizes the units it prints unless told not to - the same call emit_objects makes, so the dump - // and the object cannot disagree - if (driver.prints(Compiler::PrintKind::t_ir_units)) { - compiler.print_unit_ir(); - } - - // **unlike a store, this one is not optional.** A cache that cannot be written costs a rebuild; a - // scratch directory that cannot be written is an object with nowhere to go, so it is worth a sentence - // here rather than an ofstream failure from inside the backend - if (front.layout().prepare_scratch_dir() != Compiler::BuildDirState::t_ready) { - diagnostics.render_untyped("Cannot Build Here", fmt::format( - "'{}' could not be created, and the objects on the way to '{}' have nowhere to go.", - front.layout().scratch_dir().string(), output)); - return 1; - } - - { - Compiler::ScopedPhase phase("emit + link"); - Compiler::ProgressStep step( - Compiler::ProgressReporter::instance(), - Compiler::ProgressPhase::t_emit, - std::filesystem::path(output).filename().string()); - - // **one emit-and-link path, whichever build this is.** a second spelling - an emit_object - // plus a link_executable over the one unit `emit_objects` skips its way down to anyway, - // `link_into_main()` having consumed all the others - would drift from this one. what - // `--optimize whole` depends on is that merge, which happened above and is not in question; - // with an empty plan `object_for` answers the scratch path, which is the object that arm - // was handed. - // The tool that failed has already said what it could, and what it could not say is which - // manifest asked for each requirement - const bool linked = emit_and_link_modules(compiler, front.layout(), output, plan, link); - - // closed before the failure is reported rather than by the destructor after it, so the row sits - // above the sentence that explains it - the order every other step reaches deliberately - step.finish(linked); - - if (!linked) { - report_link_failure(diagnostics, link); - return 1; - } + if (const std::optional failed = compile_and_link_executable( + driver, diagnostics, front, bundle, compiler, output, plan, whole_program)) { + return failed.value(); } // only now, and only for what was actually emitted: a record written before the object exists would @@ -2708,6 +2791,18 @@ static int main_print_manifest( int main(int argc, char *argv[], char *envp[]) { + // before any write: on Windows this sets the console to UTF-8 and turns on virtual-terminal + // processing. A pipe is left alone; see Compiler::prepare_terminal + Compiler::prepare_terminal(); + +#if defined(_WIN32) + // echo of a string uses `_write` while integers go through `printf`. when stdout is a + // pipe it is fully buffered, and `fflush(NULL)` from JIT'd code does not drain this + // process's FILE*. unbuffer so the two writes stay in program order for `echoc run` + setvbuf(stdout, nullptr, _IONBF, 0); + setvbuf(stderr, nullptr, _IONBF, 0); +#endif + // **parse, then answer, then resolve.** Compiler::parse_command_line owns every rule about what the // words mean, including the bare `--` split, so nothing here reaches into argv - and --help and // --version come back as answers rather than as an exit taken inside a library, which is what lets diff --git a/stdlib/build/stdlib_embedded.h b/stdlib/build/stdlib_embedded.h index 8f1be48f..336dc7ee 100644 --- a/stdlib/build/stdlib_embedded.h +++ b/stdlib/build/stdlib_embedded.h @@ -32990,33 +32990,53 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x6f, 0x6d, 0x65, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x3a, 0x76, 0x69, 0x65, 0x77, - 0x3f, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x3f, 0x0a, 0x7b, 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, + 0x20, 0x3d, 0x3d, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x5d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x76, 0x61, 0x72, 0x28, 0x27, 0x55, 0x53, 0x45, 0x52, 0x50, 0x52, 0x4f, + 0x46, 0x49, 0x4c, 0x45, 0x27, 0x29, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, + 0x73, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x28, 0x27, 0x48, 0x4f, 0x4d, 0x45, - 0x27, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, - 0x2a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6f, 0x72, 0x61, 0x72, 0x79, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2c, - 0x20, 0x60, 0x2f, 0x74, 0x6d, 0x70, 0x60, 0x20, 0x77, 0x68, 0x65, 0x6e, - 0x20, 0x60, 0x54, 0x4d, 0x50, 0x44, 0x49, 0x52, 0x60, 0x20, 0x69, 0x73, - 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x74, 0x2e, 0x0a, 0x20, 0x2a, - 0x0a, 0x20, 0x2a, 0x20, 0x4e, 0x6f, 0x74, 0x20, 0x6e, 0x75, 0x6c, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, 0x62, 0x65, 0x63, 0x61, 0x75, 0x73, - 0x65, 0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x60, 0x48, 0x4f, - 0x4d, 0x45, 0x60, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x2a, 0x69, - 0x73, 0x2a, 0x20, 0x61, 0x6e, 0x20, 0x68, 0x6f, 0x6e, 0x65, 0x73, 0x74, - 0x20, 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x20, 0x77, 0x68, 0x65, 0x6e, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x20, 0x69, 0x73, 0x20, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x3a, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x2a, 0x20, 0x70, 0x61, 0x74, - 0x68, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x50, 0x4f, 0x53, 0x49, - 0x58, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x68, 0x61, 0x73, - 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6d, - 0x70, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x3a, 0x3a, 0x76, 0x69, 0x65, 0x77, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x28, - 0x27, 0x54, 0x4d, 0x50, 0x44, 0x49, 0x52, 0x27, 0x2c, 0x20, 0x27, 0x2f, - 0x74, 0x6d, 0x70, 0x27, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, + 0x27, 0x29, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x7d, + 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68, 0x65, + 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, + 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2c, 0x20, 0x60, 0x2f, 0x74, 0x6d, + 0x70, 0x60, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x60, 0x54, 0x4d, 0x50, + 0x44, 0x49, 0x52, 0x60, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x73, 0x65, 0x74, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x4e, + 0x6f, 0x74, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2c, + 0x20, 0x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65, 0x20, 0x75, 0x6e, 0x6c, + 0x69, 0x6b, 0x65, 0x20, 0x60, 0x48, 0x4f, 0x4d, 0x45, 0x60, 0x20, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x20, 0x2a, 0x69, 0x73, 0x2a, 0x20, 0x61, 0x6e, + 0x20, 0x68, 0x6f, 0x6e, 0x65, 0x73, 0x74, 0x20, 0x61, 0x6e, 0x73, 0x77, + 0x65, 0x72, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x74, 0x68, 0x65, + 0x0a, 0x20, 0x2a, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x65, 0x76, 0x65, + 0x72, 0x79, 0x20, 0x50, 0x4f, 0x53, 0x49, 0x58, 0x20, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x20, 0x68, 0x61, 0x73, 0x2e, 0x20, 0x57, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x73, 0x20, 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x73, + 0x20, 0x60, 0x54, 0x45, 0x4d, 0x50, 0x60, 0x2c, 0x20, 0x74, 0x68, 0x65, + 0x6e, 0x20, 0x60, 0x54, 0x4d, 0x50, 0x60, 0x2c, 0x20, 0x74, 0x68, 0x65, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, + 0x2d, 0x0a, 0x20, 0x2a, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, + 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x60, 0x2f, 0x74, 0x6d, 0x70, 0x60, 0x2e, + 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6d, 0x70, + 0x28, 0x29, 0x20, 0x3a, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, + 0x3a, 0x76, 0x69, 0x65, 0x77, 0x0a, 0x7b, 0x0a, 0x23, 0x5b, 0x69, 0x66, + 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x28, 0x27, 0x54, 0x45, 0x4d, + 0x50, 0x27, 0x29, 0x20, 0x3f, 0x3f, 0x20, 0x76, 0x61, 0x72, 0x28, 0x27, + 0x54, 0x4d, 0x50, 0x27, 0x2c, 0x20, 0x27, 0x2e, 0x27, 0x29, 0x3b, 0x0a, + 0x23, 0x5b, 0x65, 0x6c, 0x73, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x28, 0x27, + 0x54, 0x4d, 0x50, 0x44, 0x49, 0x52, 0x27, 0x2c, 0x20, 0x27, 0x2f, 0x74, + 0x6d, 0x70, 0x27, 0x29, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, + 0x0a, 0x7d, 0x0a, }; file_28.set_content(reinterpret_cast(file_28_data), sizeof(file_28_data)); auto &file_29 = module.add_file("stdlib:/std/env/libc.eco"); @@ -33156,133 +33176,190 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, - 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x77, 0x72, - 0x69, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x74, - 0x68, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x60, 0x24, 0x62, 0x75, 0x66, - 0x60, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, - 0x69, 0x74, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x6f, 0x72, 0x20, - 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x74, - 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x66, 0x69, - 0x74, 0x20, 0x2d, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, - 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, - 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x20, 0x60, 0x63, 0x77, 0x64, 0x60, 0x20, 0x68, 0x61, 0x73, 0x20, 0x74, - 0x6f, 0x20, 0x64, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x75, 0x69, 0x73, - 0x68, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x77, 0x68, 0x79, 0x20, 0x69, - 0x74, 0x20, 0x67, 0x72, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x73, 0x20, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65, - 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, - 0x2f, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x72, - 0x72, 0x6e, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x63, 0x77, 0x64, 0x20, - 0x61, 0x73, 0x20, 0x63, 0x5f, 0x67, 0x65, 0x74, 0x63, 0x77, 0x64, 0x28, - 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, - 0x62, 0x75, 0x66, 0x2c, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, - 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, - 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, + 0x73, 0x20, 0x21, 0x3d, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, + 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, + 0x60, 0x24, 0x62, 0x75, 0x66, 0x60, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, + 0x61, 0x6e, 0x64, 0x73, 0x20, 0x69, 0x74, 0x20, 0x62, 0x61, 0x63, 0x6b, + 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x77, 0x68, + 0x65, 0x6e, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x66, 0x69, 0x74, 0x20, 0x2d, 0x20, 0x77, 0x68, 0x69, + 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x66, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x20, 0x60, 0x63, 0x77, 0x64, 0x60, 0x20, + 0x68, 0x61, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x69, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x75, 0x69, 0x73, 0x68, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x77, 0x68, 0x79, 0x20, 0x69, 0x74, 0x20, 0x67, 0x72, 0x6f, 0x77, 0x73, + 0x20, 0x69, 0x74, 0x73, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, + 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, + 0x6e, 0x67, 0x20, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, - 0x74, 0x70, 0x69, 0x64, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x67, 0x65, - 0x74, 0x70, 0x69, 0x64, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x2a, 0x2a, - 0x74, 0x68, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x70, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x20, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2a, - 0x2a, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, - 0x2e, 0x0a, 0x2f, 0x2f, 0x0a, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x70, 0x6f, 0x72, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x22, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x22, 0x2e, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x70, - 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x68, 0x61, 0x73, 0x20, - 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x2c, - 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, - 0x6d, 0x0a, 0x2f, 0x2f, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20, - 0x61, 0x6e, 0x79, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x65, 0x6c, 0x73, - 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x64, 0x65, 0x63, - 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x6f, 0x6e, - 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x2a, 0x6c, 0x69, 0x6e, 0x6b, - 0x2a, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x72, 0x61, 0x74, 0x68, - 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, - 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, - 0x2d, 0x0a, 0x2f, 0x2f, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x68, 0x61, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x62, - 0x73, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x20, 0x74, 0x68, - 0x61, 0x74, 0x20, 0x6c, 0x61, 0x63, 0x6b, 0x20, 0x69, 0x74, 0x2c, 0x20, - 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x77, 0x68, 0x61, - 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x20, 0x62, 0x75, 0x79, 0x73, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x0a, 0x2f, - 0x2f, 0x20, 0x61, 0x72, 0x6d, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x6e, 0x6f, - 0x74, 0x20, 0x6a, 0x75, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, - 0x6f, 0x64, 0x79, 0x2e, 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, - 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x5d, - 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, - 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, - 0x2f, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x60, - 0x24, 0x62, 0x75, 0x66, 0x60, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x69, 0x74, 0x20, - 0x6e, 0x65, 0x65, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, - 0x60, 0x24, 0x73, 0x69, 0x7a, 0x65, 0x60, 0x2e, 0x20, 0x30, 0x20, 0x6f, - 0x6e, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x2d, - 0x31, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, - 0x20, 0x77, 0x61, 0x73, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x73, 0x6d, 0x61, - 0x6c, 0x6c, 0x20, 0x2d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x69, 0x74, - 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, - 0x20, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, - 0x20, 0x60, 0x24, 0x73, 0x69, 0x7a, 0x65, 0x60, 0x20, 0x77, 0x69, 0x74, - 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x2c, 0x20, 0x77, - 0x68, 0x69, 0x63, 0x68, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, - 0x69, 0x73, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x61, 0x6b, 0x65, - 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x74, 0x72, 0x79, 0x20, - 0x65, 0x78, 0x61, 0x63, 0x74, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, - 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x61, 0x20, 0x64, 0x6f, 0x75, 0x62, - 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x67, 0x75, 0x65, 0x73, 0x73, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x5f, 0x4e, 0x53, 0x47, 0x65, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x20, 0x61, 0x73, 0x20, - 0x63, 0x5f, 0x65, 0x78, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x28, 0x70, - 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, - 0x75, 0x66, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x3e, 0x20, 0x24, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x3a, - 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, - 0x65, 0x6c, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, - 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x62, 0x79, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x77, - 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x2d, - 0x31, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x2a, - 0x2a, 0x6e, 0x6f, 0x74, 0x2a, 0x2a, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, - 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, - 0x68, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, - 0x72, 0x20, 0x68, 0x65, 0x72, 0x65, 0x20, 0x68, 0x61, 0x73, 0x20, 0x74, - 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x72, 0x65, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x60, 0x73, 0x74, 0x72, - 0x3a, 0x3a, 0x66, 0x72, 0x6f, 0x6d, 0x60, 0x20, 0x77, 0x6f, 0x75, 0x6c, - 0x64, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x70, 0x61, 0x73, 0x74, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x61, - 0x64, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x72, - 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, - 0x20, 0x24, 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, - 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, - 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x73, 0x69, 0x7a, 0x65, - 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x7d, - 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, + 0x74, 0x63, 0x77, 0x64, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x67, 0x65, + 0x74, 0x63, 0x77, 0x64, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x75, 0x73, + 0x69, 0x7a, 0x65, 0x20, 0x24, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x3a, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x70, 0x69, 0x64, 0x20, 0x61, 0x73, + 0x20, 0x63, 0x5f, 0x67, 0x65, 0x74, 0x70, 0x69, 0x64, 0x28, 0x29, 0x20, + 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, + 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, + 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x73, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, + 0x67, 0x65, 0x74, 0x63, 0x77, 0x64, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, + 0x67, 0x65, 0x74, 0x63, 0x77, 0x64, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, + 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, + 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x73, 0x69, 0x7a, 0x65, 0x29, + 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, + 0x3e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x67, 0x65, 0x74, 0x70, 0x69, 0x64, 0x20, + 0x61, 0x73, 0x20, 0x63, 0x5f, 0x67, 0x65, 0x74, 0x70, 0x69, 0x64, 0x28, + 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, + 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x57, 0x20, 0x61, 0x73, 0x20, 0x63, + 0x5f, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, + 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x57, 0x28, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2c, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x3e, + 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x24, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x57, 0x69, 0x64, + 0x65, 0x43, 0x68, 0x61, 0x72, 0x54, 0x6f, 0x4d, 0x75, 0x6c, 0x74, 0x69, + 0x42, 0x79, 0x74, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x57, 0x69, + 0x64, 0x65, 0x43, 0x68, 0x61, 0x72, 0x54, 0x6f, 0x4d, 0x75, 0x6c, 0x74, + 0x69, 0x42, 0x79, 0x74, 0x65, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, + 0x6f, 0x64, 0x65, 0x70, 0x61, 0x67, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x24, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x3e, 0x20, 0x24, 0x77, + 0x69, 0x64, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, + 0x5f, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, + 0x75, 0x74, 0x66, 0x38, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x75, 0x74, 0x66, + 0x38, 0x5f, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, + 0x24, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x68, 0x61, + 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, + 0x74, 0x72, 0x3c, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, 0x20, 0x24, 0x75, + 0x73, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x43, 0x50, 0x5f, 0x55, 0x54, 0x46, 0x38, 0x20, + 0x3d, 0x20, 0x36, 0x35, 0x30, 0x30, 0x31, 0x3b, 0x0a, 0x23, 0x5b, 0x65, + 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x2a, 0x2a, 0x74, 0x68, + 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, + 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2a, 0x2a, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x2e, 0x0a, + 0x2f, 0x2f, 0x0a, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, + 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x22, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x22, 0x2e, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x70, 0x6c, 0x61, + 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x68, 0x61, 0x73, 0x20, 0x65, 0x78, + 0x61, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x2c, 0x20, 0x6e, + 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x0a, + 0x2f, 0x2f, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6e, + 0x79, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x69, 0x73, 0x20, 0x61, 0x20, 0x2a, 0x6c, 0x69, 0x6e, 0x6b, 0x2a, 0x20, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x69, 0x6c, 0x65, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x2d, 0x0a, + 0x2f, 0x2f, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, + 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x61, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x62, 0x73, 0x65, + 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x6c, 0x61, 0x63, 0x6b, 0x20, 0x69, 0x74, 0x2c, 0x20, 0x77, 0x68, + 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x62, + 0x75, 0x79, 0x73, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x0a, 0x2f, 0x2f, 0x20, + 0x61, 0x72, 0x6d, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x6a, 0x75, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6f, 0x64, + 0x79, 0x2e, 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, + 0x3d, 0x3d, 0x20, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x5d, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, + 0x61, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x60, 0x24, 0x62, + 0x75, 0x66, 0x60, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x69, 0x74, 0x20, 0x6e, 0x65, + 0x65, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x60, 0x24, + 0x73, 0x69, 0x7a, 0x65, 0x60, 0x2e, 0x20, 0x30, 0x20, 0x6f, 0x6e, 0x20, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x2d, 0x31, 0x20, + 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x77, + 0x61, 0x73, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, + 0x20, 0x2d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x69, 0x74, 0x20, 0x68, + 0x61, 0x73, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x6f, + 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x60, + 0x24, 0x73, 0x69, 0x7a, 0x65, 0x60, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x68, 0x69, + 0x63, 0x68, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x73, + 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x74, 0x72, 0x79, 0x20, 0x65, 0x78, + 0x61, 0x63, 0x74, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x61, 0x6e, 0x20, 0x61, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x69, + 0x6e, 0x67, 0x20, 0x67, 0x75, 0x65, 0x73, 0x73, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4e, + 0x53, 0x47, 0x65, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, + 0x65, 0x78, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x28, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, + 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x3e, 0x20, 0x24, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6c, + 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x6c, 0x69, + 0x6e, 0x75, 0x78, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x79, + 0x74, 0x65, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x77, 0x72, 0x69, + 0x74, 0x74, 0x65, 0x6e, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x2e, + 0x20, 0x49, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x2a, 0x2a, 0x6e, + 0x6f, 0x74, 0x2a, 0x2a, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, + 0x74, 0x65, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x68, 0x69, + 0x6e, 0x67, 0x20, 0x61, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, + 0x68, 0x65, 0x72, 0x65, 0x20, 0x68, 0x61, 0x73, 0x20, 0x74, 0x6f, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x72, 0x65, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x60, 0x73, 0x74, 0x72, 0x3a, 0x3a, + 0x66, 0x72, 0x6f, 0x6d, 0x60, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, + 0x72, 0x65, 0x61, 0x64, 0x20, 0x70, 0x61, 0x73, 0x74, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6c, + 0x69, 0x6e, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x72, 0x65, 0x61, + 0x64, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, + 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x75, + 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, + 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, + 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, }; file_29.set_content(reinterpret_cast(file_29_data), sizeof(file_29_data)); auto &file_30 = module.add_file("stdlib:/std/env/process.eco"); @@ -34011,55 +34088,167 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x66, 0x69, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x36, 0x34, 0x20, 0x4b, 0x69, - 0x42, 0x27, 0x29, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x73, 0x65, 0x5d, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x73, 0x74, 0x72, 0x3a, 0x3a, 0x66, 0x72, 0x6f, 0x6d, 0x28, 0x61, 0x72, - 0x67, 0x28, 0x30, 0x29, 0x2d, 0x3e, 0x63, 0x73, 0x74, 0x72, 0x28, 0x29, - 0x29, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x7d, 0x0a, - 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x53, 0x74, 0x6f, 0x70, - 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, - 0x6d, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x60, 0x24, 0x63, 0x6f, 0x64, - 0x65, 0x60, 0x2c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x68, 0x65, - 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x4e, 0x6f, - 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x72, 0x75, 0x6e, - 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x6e, 0x6f, 0x20, 0x64, 0x65, 0x73, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x72, 0x75, 0x6e, 0x73, - 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, - 0x6e, 0x6f, 0x20, 0x60, 0x5b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5d, - 0x60, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x73, 0x20, - 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x2d, 0x0a, 0x20, 0x2a, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x62, 0x61, - 0x72, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x60, 0x64, 0x69, 0x65, 0x60, 0x20, - 0x6d, 0x61, 0x6b, 0x65, 0x73, 0x2c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x3a, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, - 0x20, 0x6e, 0x6f, 0x20, 0x70, 0x6f, 0x73, 0x74, 0x2d, 0x74, 0x65, 0x61, - 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x20, 0x6d, 0x6f, 0x6d, 0x65, 0x6e, 0x74, - 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, - 0x68, 0x65, 0x0a, 0x20, 0x2a, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x69, - 0x73, 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x20, 0x6f, - 0x66, 0x20, 0x61, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, - 0x61, 0x74, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x6f, 0x77, 0x6e, - 0x73, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x20, 0x52, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, - 0x60, 0x6d, 0x61, 0x69, 0x6e, 0x60, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, - 0x61, 0x64, 0x20, 0x69, 0x66, 0x0a, 0x20, 0x2a, 0x20, 0x79, 0x6f, 0x75, - 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x6c, 0x79, 0x20, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x2e, - 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x65, - 0x78, 0x69, 0x74, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, - 0x77, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x68, 0x65, - 0x6c, 0x6c, 0x20, 0x73, 0x65, 0x65, 0x73, 0x2e, 0x20, 0x60, 0x30, 0x60, - 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x23, 0x5b, - 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x3a, 0x20, 0x65, 0x78, 0x69, - 0x74, 0x5d, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x66, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x74, 0x28, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, 0x6f, 0x64, 0x65, 0x29, - 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x3b, 0x0a, + 0x42, 0x27, 0x29, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x69, 0x66, 0x3a, + 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x73, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, + 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x57, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, + 0x20, 0x55, 0x54, 0x46, 0x2d, 0x31, 0x36, 0x2e, 0x20, 0x67, 0x72, 0x6f, + 0x77, 0x20, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x20, 0x69, 0x74, 0x20, 0x66, + 0x69, 0x74, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x57, 0x69, + 0x64, 0x65, 0x43, 0x68, 0x61, 0x72, 0x54, 0x6f, 0x4d, 0x75, 0x6c, 0x74, + 0x69, 0x42, 0x79, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x2c, 0x20, 0x77, + 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x77, 0x68, 0x61, 0x74, + 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x45, 0x63, 0x68, 0x6f, 0x20, + 0x60, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x60, 0x20, 0x69, 0x73, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, + 0x77, 0x69, 0x64, 0x65, 0x5f, 0x63, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x32, + 0x35, 0x36, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, + 0x6c, 0x65, 0x20, 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, 0x5f, 0x63, 0x61, + 0x70, 0x20, 0x3c, 0x3d, 0x20, 0x33, 0x32, 0x37, 0x36, 0x38, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, + 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x3e, 0x20, 0x24, 0x77, + 0x69, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x3e, + 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, 0x5f, 0x63, 0x61, 0x70, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x24, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, + 0x20, 0x3d, 0x20, 0x63, 0x5f, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x57, 0x28, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x2c, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x2c, + 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x5f, 0x63, 0x61, 0x70, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x24, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, + 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, + 0x66, 0x72, 0x65, 0x65, 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x64, 0x69, 0x65, 0x28, 0x27, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x57, + 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x27, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x77, + 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3e, 0x3d, 0x20, 0x24, 0x77, + 0x69, 0x64, 0x65, 0x5f, 0x63, 0x61, 0x70, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x65, 0x6d, 0x3a, 0x3a, 0x66, 0x72, 0x65, 0x65, 0x28, 0x24, 0x77, 0x69, + 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x5f, 0x63, + 0x61, 0x70, 0x20, 0x3d, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x5f, 0x63, + 0x61, 0x70, 0x20, 0x2a, 0x20, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x69, 0x6e, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, + 0x5f, 0x6e, 0x20, 0x3d, 0x20, 0x63, 0x5f, 0x57, 0x69, 0x64, 0x65, 0x43, + 0x68, 0x61, 0x72, 0x54, 0x6f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x42, 0x79, + 0x74, 0x65, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x43, 0x50, 0x5f, 0x55, 0x54, 0x46, 0x38, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x24, + 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, + 0x3e, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x2c, 0x20, 0x70, 0x74, 0x72, + 0x3c, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, 0x28, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x75, 0x74, 0x66, 0x38, 0x5f, 0x6e, + 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, + 0x3a, 0x3a, 0x66, 0x72, 0x65, 0x65, 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x64, 0x69, 0x65, 0x28, 0x27, 0x57, 0x69, 0x64, 0x65, + 0x43, 0x68, 0x61, 0x72, 0x54, 0x6f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x42, + 0x79, 0x74, 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x27, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x75, 0x74, 0x66, + 0x38, 0x20, 0x3d, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x61, 0x6c, 0x6c, + 0x6f, 0x63, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x28, 0x24, + 0x75, 0x74, 0x66, 0x38, 0x5f, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x29, 0x20, + 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, + 0x3a, 0x24, 0x5b, 0x24, 0x75, 0x74, 0x66, 0x38, 0x5f, 0x6e, 0x5d, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x5f, 0x57, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x72, 0x54, + 0x6f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x42, 0x79, 0x74, 0x65, 0x28, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x43, 0x50, 0x5f, 0x55, 0x54, 0x46, 0x38, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x24, 0x77, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x24, 0x77, 0x72, 0x69, 0x74, + 0x74, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, 0x2c, 0x20, 0x24, 0x75, 0x74, + 0x66, 0x38, 0x5f, 0x6e, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x2c, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, 0x28, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x66, 0x72, 0x65, 0x65, + 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x20, 0x24, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, + 0x3a, 0x3a, 0x66, 0x72, 0x6f, 0x6d, 0x28, 0x24, 0x75, 0x74, 0x66, 0x38, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x65, 0x6d, 0x3a, 0x3a, 0x66, 0x72, 0x65, 0x65, 0x28, 0x24, 0x75, 0x74, + 0x66, 0x38, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x70, 0x61, + 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x64, 0x69, 0x65, 0x28, 0x27, 0x74, 0x68, 0x65, 0x20, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x70, + 0x61, 0x74, 0x68, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x66, 0x69, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x33, 0x32, 0x20, 0x4b, + 0x69, 0x42, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x54, 0x46, 0x2d, 0x31, 0x36, + 0x27, 0x29, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x73, 0x65, 0x5d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, + 0x74, 0x72, 0x3a, 0x3a, 0x66, 0x72, 0x6f, 0x6d, 0x28, 0x61, 0x72, 0x67, + 0x28, 0x30, 0x29, 0x2d, 0x3e, 0x63, 0x73, 0x74, 0x72, 0x28, 0x29, 0x29, + 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x7d, 0x0a, 0x0a, + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x53, 0x74, 0x6f, 0x70, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x60, 0x24, 0x63, 0x6f, 0x64, 0x65, + 0x60, 0x2c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x68, 0x65, 0x72, + 0x65, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x4e, 0x6f, 0x74, + 0x68, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x72, 0x75, 0x6e, 0x73, + 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x6e, 0x6f, 0x20, 0x64, 0x65, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x72, 0x75, 0x6e, 0x73, 0x20, + 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6e, + 0x6f, 0x20, 0x60, 0x5b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5d, 0x60, + 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x73, 0x20, 0x70, + 0x72, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x2d, 0x0a, 0x20, 0x2a, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x62, 0x61, 0x72, + 0x67, 0x61, 0x69, 0x6e, 0x20, 0x60, 0x64, 0x69, 0x65, 0x60, 0x20, 0x6d, + 0x61, 0x6b, 0x65, 0x73, 0x2c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x3a, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, + 0x6e, 0x6f, 0x20, 0x70, 0x6f, 0x73, 0x74, 0x2d, 0x74, 0x65, 0x61, 0x72, + 0x64, 0x6f, 0x77, 0x6e, 0x20, 0x6d, 0x6f, 0x6d, 0x65, 0x6e, 0x74, 0x20, + 0x6c, 0x65, 0x66, 0x74, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x0a, 0x20, 0x2a, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x69, 0x73, + 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x20, 0x6f, 0x66, + 0x20, 0x61, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x6f, 0x77, 0x6e, 0x73, + 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x20, 0x52, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x60, + 0x6d, 0x61, 0x69, 0x6e, 0x60, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, + 0x64, 0x20, 0x69, 0x66, 0x0a, 0x20, 0x2a, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x77, 0x61, 0x6e, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x6c, 0x79, 0x20, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x2e, 0x0a, + 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x65, 0x78, + 0x69, 0x74, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x77, + 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x68, 0x65, 0x6c, + 0x6c, 0x20, 0x73, 0x65, 0x65, 0x73, 0x2e, 0x20, 0x60, 0x30, 0x60, 0x20, + 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x23, 0x5b, 0x62, + 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x3a, 0x20, 0x65, 0x78, 0x69, 0x74, + 0x5d, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x74, 0x28, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x20, + 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x3b, 0x0a, }; file_30.set_content(reinterpret_cast(file_30_data), sizeof(file_30_data)); auto &file_31 = module.add_file("stdlib:/std/env/vars.eco"); @@ -35013,8 +35202,365 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x6f, 0x6b, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; file_32.set_content(reinterpret_cast(file_32_data), sizeof(file_32_data)); - auto &file_33 = module.add_file("stdlib:/std/io/error.eco"); + auto &file_33 = module.add_file("stdlib:/std/io/console.eco"); static const unsigned char file_33_data[] = { + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x72, 0x69, 0x74, 0x69, + 0x6e, 0x67, 0x20, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x20, 0x74, 0x6f, 0x20, + 0x61, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, + 0x20, 0x55, 0x6e, 0x69, 0x78, 0x20, 0x60, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x28, 0x32, 0x29, 0x60, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, + 0x6c, 0x20, 0x64, 0x72, 0x61, 0x77, 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x63, 0x65, 0x6c, 0x6c, 0x0a, 0x20, 0x2a, 0x20, 0x70, 0x65, 0x72, 0x20, + 0x55, 0x54, 0x46, 0x2d, 0x38, 0x20, 0x67, 0x6c, 0x79, 0x70, 0x68, 0x2e, + 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x60, 0x5f, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x60, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x0a, + 0x20, 0x2a, 0x20, 0x70, 0x61, 0x67, 0x65, 0x20, 0x28, 0x38, 0x35, 0x30, + 0x2c, 0x20, 0x34, 0x33, 0x37, 0x29, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, + 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x73, 0x20, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x2c, + 0x20, 0x73, 0x6f, 0x20, 0x61, 0x20, 0x67, 0x6c, 0x79, 0x70, 0x68, 0x20, + 0x74, 0x77, 0x6f, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x72, 0x65, 0x65, + 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x20, 0x2a, 0x20, 0x77, 0x69, + 0x64, 0x65, 0x20, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x61, 0x73, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x61, 0x6e, 0x79, 0x20, 0x43, 0x50, + 0x38, 0x35, 0x30, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x70, 0x69, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x73, 0x20, 0x61, + 0x70, 0x61, 0x72, 0x74, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x0a, 0x20, 0x2a, + 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x20, + 0x77, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x20, 0x55, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x6f, 0x20, + 0x61, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x60, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x73, 0x6f, + 0x6c, 0x65, 0x57, 0x60, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x20, 0x6f, 0x66, 0x20, 0x60, 0x63, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x60, 0x2e, 0x20, 0x41, 0x20, 0x70, 0x69, 0x70, 0x65, 0x20, 0x6f, 0x72, + 0x20, 0x61, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, + 0x73, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x73, 0x6f, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x60, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x60, 0x63, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x60, 0x20, + 0x66, 0x61, 0x6c, 0x6c, 0x73, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, + 0x68, 0x20, 0x74, 0x6f, 0x20, 0x60, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x60, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, + 0x65, 0x20, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x0a, 0x20, 0x2a, 0x20, 0x2d, + 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x77, 0x68, + 0x61, 0x74, 0x20, 0x61, 0x20, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, + 0x60, 0x74, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x65, 0x63, 0x6f, 0x2f, 0x69, + 0x6f, 0x60, 0x20, 0x67, 0x6f, 0x6c, 0x64, 0x65, 0x6e, 0x20, 0x73, 0x65, + 0x65, 0x2e, 0x20, 0x4e, 0x6f, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x50, 0x49, 0x3a, 0x20, 0x60, 0x73, 0x74, + 0x64, 0x6f, 0x75, 0x74, 0x2d, 0x3e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x60, + 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x2e, 0x0a, 0x20, + 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x69, 0x6f, 0x3b, 0x0a, 0x0a, + 0x23, 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x43, 0x4f, 0x4e, 0x53, 0x4f, + 0x4c, 0x45, 0x5f, 0x43, 0x50, 0x5f, 0x55, 0x54, 0x46, 0x38, 0x20, 0x3d, + 0x20, 0x36, 0x35, 0x30, 0x30, 0x31, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, + 0x5f, 0x56, 0x49, 0x52, 0x54, 0x55, 0x41, 0x4c, 0x5f, 0x54, 0x45, 0x52, + 0x4d, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, + 0x53, 0x49, 0x4e, 0x47, 0x20, 0x3d, 0x20, 0x34, 0x3b, 0x0a, 0x0a, 0x2f, + 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x24, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x60, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, + 0x55, 0x54, 0x46, 0x2d, 0x38, 0x20, 0x61, 0x74, 0x20, 0x60, 0x24, 0x62, + 0x75, 0x66, 0x60, 0x2c, 0x20, 0x64, 0x72, 0x61, 0x77, 0x6e, 0x20, 0x6f, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, + 0x65, 0x20, 0x60, 0x24, 0x66, 0x64, 0x60, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x0a, 0x20, 0x2a, 0x20, + 0x77, 0x68, 0x65, 0x6e, 0x20, 0x60, 0x24, 0x66, 0x64, 0x60, 0x20, 0x69, + 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x6f, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x20, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x57, 0x43, 0x48, 0x41, 0x52, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, + 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x57, 0x60, 0x20, 0x72, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x60, 0x72, 0x61, 0x77, + 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x60, 0x20, + 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x74, + 0x72, 0x79, 0x20, 0x61, 0x74, 0x20, 0x61, 0x20, 0x55, 0x54, 0x46, 0x2d, + 0x31, 0x36, 0x0a, 0x20, 0x2a, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, 0x2c, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, + 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x29, 0x20, 0x3a, 0x20, 0x69, 0x73, 0x69, 0x7a, 0x65, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x20, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, + 0x20, 0x63, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x6f, 0x73, 0x66, 0x68, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x28, 0x24, 0x66, 0x64, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x6d, + 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x5f, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x24, + 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x26, 0x24, 0x6d, 0x6f, + 0x64, 0x65, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x73, 0x6f, 0x20, + 0x60, 0x5c, 0x78, 0x31, 0x62, 0x5b, 0x48, 0x60, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x60, 0x5c, 0x78, 0x31, 0x62, 0x5b, 0x32, 0x4a, 0x60, 0x20, 0x6d, + 0x6f, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x73, + 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6f, + 0x66, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x61, + 0x73, 0x20, 0x60, 0xe2, 0x86, 0x90, 0x5b, 0x48, 0x60, 0x2e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x20, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x62, 0x69, 0x74, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x6e, + 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x20, 0x73, + 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x2d, 0x20, 0x73, 0x74, 0x69, 0x63, + 0x6b, 0x79, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x43, 0x74, 0x72, + 0x6c, 0x2d, 0x43, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x65, 0x6e, + 0x69, 0x67, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x53, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, + 0x28, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x24, 0x6d, + 0x6f, 0x64, 0x65, 0x20, 0x7c, 0x20, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, + 0x5f, 0x56, 0x49, 0x52, 0x54, 0x55, 0x41, 0x4c, 0x5f, 0x54, 0x45, 0x52, + 0x4d, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, + 0x53, 0x49, 0x4e, 0x47, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x3d, + 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x73, + 0x6f, 0x6c, 0x65, 0x57, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, + 0x74, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x20, + 0x60, 0x5c, 0x6e, 0x60, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, 0x79, + 0x20, 0x55, 0x43, 0x52, 0x54, 0x20, 0x60, 0x5f, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x60, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x64, + 0x6f, 0x65, 0x73, 0x2e, 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6e, 0x65, 0x20, + 0x4c, 0x46, 0x20, 0x77, 0x61, 0x6c, 0x6b, 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x20, 0x64, 0x6f, 0x77, 0x6e, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x20, 0x73, 0x6b, 0x69, 0x70, 0x20, 0x61, + 0x6e, 0x20, 0x4c, 0x46, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x70, 0x72, 0x65, + 0x63, 0x65, 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x43, 0x52, 0x2c, + 0x20, 0x73, 0x6f, 0x20, 0x61, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, + 0x20, 0x77, 0x68, 0x6f, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x52, 0x4c, 0x46, 0x20, 0x69, 0x73, + 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x64, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x20, + 0x28, 0x30, 0x20, 0x2e, 0x2e, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x20, 0x61, 0x73, 0x20, 0x24, 0x69, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x62, + 0x75, 0x66, 0x3a, 0x24, 0x5b, 0x24, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x20, + 0x31, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x69, + 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x24, 0x65, 0x78, 0x74, 0x72, 0x61, 0x20, 0x3d, 0x20, 0x24, 0x65, + 0x78, 0x74, 0x72, 0x61, 0x20, 0x2b, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x24, 0x62, 0x75, 0x66, 0x3a, 0x24, 0x5b, 0x24, 0x69, + 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x21, 0x3d, 0x20, 0x31, 0x33, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x20, 0x3d, 0x20, 0x24, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x20, 0x2b, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, 0x20, + 0x3d, 0x20, 0x24, 0x62, 0x75, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, 0x5f, + 0x6e, 0x20, 0x3d, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x61, + 0x73, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x20, 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x3d, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x24, 0x65, 0x78, 0x74, 0x72, 0x61, 0x20, 0x21, + 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, + 0x3a, 0x24, 0x20, 0x3d, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x61, 0x6c, + 0x6c, 0x6f, 0x63, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x24, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x2b, 0x20, 0x24, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, + 0x64, 0x65, 0x64, 0x3a, 0x24, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, + 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x20, 0x28, 0x30, 0x20, 0x2e, 0x2e, + 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x61, 0x73, 0x20, 0x24, + 0x69, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x20, 0x24, + 0x62, 0x20, 0x3d, 0x20, 0x24, 0x62, 0x75, 0x66, 0x3a, 0x24, 0x5b, 0x24, + 0x69, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x62, 0x20, + 0x3d, 0x3d, 0x20, 0x31, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x30, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x3a, 0x24, 0x5b, + 0x24, 0x61, 0x74, 0x5d, 0x20, 0x3d, 0x20, 0x31, 0x33, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x61, 0x74, 0x20, 0x3d, + 0x20, 0x24, 0x61, 0x74, 0x20, 0x2b, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x24, 0x62, 0x75, 0x66, 0x3a, 0x24, 0x5b, 0x24, 0x69, + 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x21, 0x3d, 0x20, 0x31, 0x33, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, + 0x3a, 0x24, 0x5b, 0x24, 0x61, 0x74, 0x5d, 0x20, 0x3d, 0x20, 0x31, 0x33, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x24, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x24, 0x61, 0x74, 0x20, + 0x2b, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x3a, 0x24, 0x5b, + 0x24, 0x61, 0x74, 0x5d, 0x20, 0x3d, 0x20, 0x24, 0x62, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, + 0x61, 0x74, 0x20, 0x3d, 0x20, 0x24, 0x61, 0x74, 0x20, 0x2b, 0x20, 0x31, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x75, 0x74, + 0x66, 0x38, 0x3a, 0x24, 0x20, 0x3d, 0x20, 0x24, 0x65, 0x78, 0x70, 0x61, + 0x6e, 0x64, 0x65, 0x64, 0x3a, 0x24, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, 0x5f, 0x6e, 0x20, + 0x3d, 0x20, 0x24, 0x61, 0x74, 0x20, 0x61, 0x73, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x77, 0x69, + 0x64, 0x65, 0x5f, 0x6e, 0x20, 0x3d, 0x20, 0x63, 0x5f, 0x4d, 0x75, 0x6c, + 0x74, 0x69, 0x42, 0x79, 0x74, 0x65, 0x54, 0x6f, 0x57, 0x69, 0x64, 0x65, + 0x43, 0x68, 0x61, 0x72, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x5f, 0x43, 0x50, + 0x5f, 0x55, 0x54, 0x46, 0x38, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x24, 0x75, + 0x74, 0x66, 0x38, 0x2c, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, 0x5f, 0x6e, + 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, + 0x3e, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x77, + 0x69, 0x64, 0x65, 0x5f, 0x6e, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x3a, + 0x24, 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x66, 0x72, 0x65, 0x65, 0x28, 0x24, + 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x31, 0x36, 0x3e, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x20, 0x3d, 0x20, + 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x3c, 0x75, + 0x69, 0x6e, 0x74, 0x31, 0x36, 0x3e, 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, + 0x5f, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x29, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, + 0x77, 0x69, 0x64, 0x65, 0x3a, 0x24, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, + 0x64, 0x65, 0x64, 0x3a, 0x24, 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, + 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x66, 0x72, + 0x65, 0x65, 0x28, 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x4d, 0x75, + 0x6c, 0x74, 0x69, 0x42, 0x79, 0x74, 0x65, 0x54, 0x6f, 0x57, 0x69, 0x64, + 0x65, 0x43, 0x68, 0x61, 0x72, 0x28, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, + 0x45, 0x5f, 0x43, 0x50, 0x5f, 0x55, 0x54, 0x46, 0x38, 0x2c, 0x20, 0x30, + 0x2c, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, 0x2c, 0x20, 0x24, 0x75, 0x74, + 0x66, 0x38, 0x5f, 0x6e, 0x2c, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x2c, + 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x5f, 0x6e, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x65, 0x78, 0x70, + 0x61, 0x6e, 0x64, 0x65, 0x64, 0x3a, 0x24, 0x20, 0x21, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x66, 0x72, 0x65, 0x65, + 0x28, 0x24, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x6f, 0x66, 0x66, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x24, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x24, 0x77, + 0x69, 0x64, 0x65, 0x5f, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, + 0x7a, 0x65, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, + 0x6c, 0x65, 0x20, 0x28, 0x24, 0x6f, 0x66, 0x66, 0x20, 0x3c, 0x20, 0x24, + 0x6e, 0x65, 0x65, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, + 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x20, 0x3d, + 0x20, 0x28, 0x24, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x2d, 0x20, 0x24, 0x6f, + 0x66, 0x66, 0x29, 0x20, 0x61, 0x73, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x6f, 0x6b, 0x20, 0x3d, 0x20, 0x63, + 0x5f, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, + 0x65, 0x57, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, + 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x3a, 0x24, 0x20, 0x2b, 0x20, 0x24, + 0x6f, 0x66, 0x66, 0x2c, 0x20, 0x24, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x2c, + 0x20, 0x26, 0x24, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x2c, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x6f, 0x6b, 0x20, + 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x3a, + 0x3a, 0x66, 0x72, 0x65, 0x65, 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x24, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x3d, 0x20, + 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x66, 0x72, + 0x65, 0x65, 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x6f, 0x66, 0x66, 0x20, 0x3d, 0x20, + 0x24, 0x6f, 0x66, 0x66, 0x20, 0x2b, 0x20, 0x24, 0x77, 0x72, 0x69, 0x74, + 0x74, 0x65, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x66, 0x72, 0x65, + 0x65, 0x28, 0x24, 0x77, 0x69, 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x61, 0x73, 0x20, 0x69, 0x73, 0x69, 0x7a, + 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, + }; + file_33.set_content(reinterpret_cast(file_33_data), sizeof(file_33_data)); + auto &file_34 = module.add_file("stdlib:/std/io/error.eco"); + static const unsigned char file_34_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x68, 0x79, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x2f, 0x4f, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, @@ -35137,52 +35683,55 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x6f, 0x66, 0x28, 0x66, 0x69, 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x24, 0x6d, 0x6f, 0x64, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x28, - 0x24, 0x6d, 0x6f, 0x64, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x6d, 0x6f, 0x64, - 0x65, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x3d, 0x3e, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x4f, 0x4e, 0x4c, 0x59, - 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, - 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, - 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x3a, 0x3a, 0x77, 0x72, 0x69, 0x74, - 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x3e, 0x20, 0x4f, 0x5f, 0x57, - 0x52, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, 0x52, - 0x45, 0x41, 0x54, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, - 0x43, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, - 0x43, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x69, 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x3a, 0x3a, 0x61, 0x70, 0x70, - 0x65, 0x6e, 0x64, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x3e, 0x20, 0x4f, 0x5f, - 0x57, 0x52, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, - 0x52, 0x45, 0x41, 0x54, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x41, 0x50, 0x50, - 0x45, 0x4e, 0x44, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, 0x4c, 0x4f, 0x45, - 0x58, 0x45, 0x43, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x3d, + 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x28, 0x24, 0x6d, 0x6f, 0x64, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x3a, 0x3a, 0x72, - 0x65, 0x61, 0x64, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x3d, 0x3e, 0x20, - 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, - 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x77, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6f, 0x66, 0x28, 0x77, 0x68, - 0x65, 0x6e, 0x63, 0x65, 0x20, 0x24, 0x66, 0x72, 0x6f, 0x6d, 0x29, 0x20, - 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x61, 0x74, - 0x63, 0x68, 0x20, 0x28, 0x24, 0x66, 0x72, 0x6f, 0x6d, 0x29, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x65, - 0x6e, 0x63, 0x65, 0x3a, 0x3a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x20, - 0x20, 0x3d, 0x3e, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, - 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, - 0x65, 0x6e, 0x63, 0x65, 0x3a, 0x3a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x20, 0x3d, 0x3e, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x43, 0x55, - 0x52, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, - 0x68, 0x65, 0x6e, 0x63, 0x65, 0x3a, 0x3a, 0x65, 0x6e, 0x64, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x3d, 0x3e, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x45, - 0x4e, 0x44, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x7d, - 0x0a, + 0x65, 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x3e, 0x20, + 0x4f, 0x5f, 0x52, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x7c, 0x20, 0x4f, + 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x6d, 0x6f, + 0x64, 0x65, 0x3a, 0x3a, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x3e, 0x20, 0x4f, 0x5f, 0x57, 0x52, 0x4f, 0x4e, 0x4c, + 0x59, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x20, + 0x7c, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x20, 0x7c, 0x20, + 0x4f, 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x6d, + 0x6f, 0x64, 0x65, 0x3a, 0x3a, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x3e, 0x20, 0x4f, 0x5f, 0x57, 0x52, 0x4f, 0x4e, + 0x4c, 0x59, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, + 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, + 0x7c, 0x20, 0x4f, 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6c, + 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x20, 0x3d, 0x3e, 0x20, 0x4f, 0x5f, 0x52, 0x44, + 0x57, 0x52, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, + 0x45, 0x43, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, + 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x7c, 0x20, 0x4f, 0x5f, 0x42, 0x49, + 0x4e, 0x41, 0x52, 0x59, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6f, 0x66, + 0x28, 0x77, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x24, 0x66, 0x72, 0x6f, + 0x6d, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x28, 0x24, 0x66, 0x72, 0x6f, 0x6d, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x77, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x3a, 0x3a, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x20, 0x20, 0x20, 0x3d, 0x3e, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, + 0x53, 0x45, 0x54, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x77, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x3a, 0x3a, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x3e, 0x20, 0x53, 0x45, 0x45, 0x4b, + 0x5f, 0x43, 0x55, 0x52, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x3a, 0x3a, 0x65, 0x6e, + 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x3e, 0x20, 0x53, 0x45, 0x45, + 0x4b, 0x5f, 0x45, 0x4e, 0x44, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x7d, 0x0a, }; - file_33.set_content(reinterpret_cast(file_33_data), sizeof(file_33_data)); - auto &file_34 = module.add_file("stdlib:/std/io/file.eco"); - static const unsigned char file_34_data[] = { + file_34.set_content(reinterpret_cast(file_34_data), sizeof(file_34_data)); + auto &file_35 = module.add_file("stdlib:/std/io/file.eco"); + static const unsigned char file_35_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x6e, 0x20, 0x6f, 0x77, 0x6e, 0x65, 0x64, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x77, 0x69, 0x74, @@ -35697,9 +36246,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x75, 0x72, 0x6e, 0x20, 0x2e, 0x6f, 0x6b, 0x28, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; - file_34.set_content(reinterpret_cast(file_34_data), sizeof(file_34_data)); - auto &file_35 = module.add_file("stdlib:/std/io/from.eco"); - static const unsigned char file_35_data[] = { + file_35.set_content(reinterpret_cast(file_35_data), sizeof(file_35_data)); + auto &file_36 = module.add_file("stdlib:/std/io/from.eco"); + static const unsigned char file_36_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x73, 0x74, 0x72, 0x3a, 0x3a, 0x66, 0x72, 0x6f, 0x6d, 0x60, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x60, 0x69, 0x6f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x60, 0x2c, 0x20, 0x73, 0x6f, @@ -35726,9 +36275,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x65, 0x2d, 0x3e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; - file_35.set_content(reinterpret_cast(file_35_data), sizeof(file_35_data)); - auto &file_36 = module.add_file("stdlib:/std/io/libc.eco"); - static const unsigned char file_36_data[] = { + file_36.set_content(reinterpret_cast(file_36_data), sizeof(file_36_data)); + auto &file_37 = module.add_file("stdlib:/std/io/libc.eco"); + static const unsigned char file_37_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x45, 0x76, 0x65, 0x72, 0x79, 0x20, 0x43, 0x20, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x62, 0x69, @@ -35794,118 +36343,206 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x61, 0x64, 0x2c, 0x20, 0x73, 0x61, 0x69, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x73, 0x74, - 0x64, 0x3a, 0x3a, 0x69, 0x6f, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x72, 0x61, 0x77, 0x2c, 0x20, 0x75, 0x6e, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x65, 0x64, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, 0x20, - 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x61, 0x64, 0x73, 0x2e, 0x20, 0x64, - 0x65, 0x6c, 0x69, 0x62, 0x65, 0x72, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x20, - 0x2a, 0x2a, 0x6e, 0x6f, 0x74, 0x2a, 0x2a, 0x20, 0x60, 0x66, 0x77, 0x72, - 0x69, 0x74, 0x65, 0x60, 0x2f, 0x60, 0x66, 0x72, 0x65, 0x61, 0x64, 0x60, - 0x3a, 0x20, 0x61, 0x20, 0x60, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x60, - 0x20, 0x6e, 0x65, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, - 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x4e, 0x55, 0x4c, 0x2d, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x2c, 0x20, - 0x69, 0x74, 0x73, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x69, - 0x73, 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x6b, 0x6e, 0x6f, - 0x77, 0x6e, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x20, 0x6f, - 0x66, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, - 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x6c, - 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, - 0x20, 0x69, 0x73, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x70, - 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x64, 0x69, 0x65, 0x73, 0x0a, + 0x64, 0x3a, 0x3a, 0x69, 0x6f, 0x3b, 0x0a, 0x0a, 0x23, 0x5b, 0x69, 0x66, + 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x61, + 0x77, 0x2c, 0x20, 0x75, 0x6e, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x65, + 0x64, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x72, 0x65, 0x61, 0x64, 0x73, 0x2e, 0x20, 0x64, 0x65, 0x6c, 0x69, + 0x62, 0x65, 0x72, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x20, 0x2a, 0x2a, 0x6e, + 0x6f, 0x74, 0x2a, 0x2a, 0x20, 0x60, 0x66, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x60, 0x2f, 0x60, 0x66, 0x72, 0x65, 0x61, 0x64, 0x60, 0x3a, 0x20, 0x61, + 0x20, 0x60, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x60, 0x20, 0x6e, 0x65, + 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6e, 0x6f, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x4e, 0x55, 0x4c, 0x2d, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x69, 0x74, 0x73, + 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x69, 0x73, 0x20, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x20, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x75, 0x6e, 0x64, + 0x65, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x77, 0x6f, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x74, + 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x20, 0x64, 0x69, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, + 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, + 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x72, + 0x65, 0x61, 0x64, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, + 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, + 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x75, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x20, 0x3a, 0x20, + 0x69, 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x60, 0x66, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x28, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x60, 0x20, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x65, + 0x73, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x73, 0x74, 0x64, 0x69, + 0x6f, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2c, 0x20, 0x77, 0x68, + 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, + 0x6e, 0x65, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x65, 0x65, + 0x64, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x73, 0x74, 0x64, 0x69, 0x6f, 0x3a, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x60, 0x65, 0x63, 0x68, 0x6f, 0x60, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x73, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x60, 0x70, + 0x72, 0x69, 0x6e, 0x74, 0x66, 0x60, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x20, 0x69, 0x73, 0x20, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x66, 0x6c, 0x75, + 0x73, 0x68, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x66, 0x66, 0x6c, 0x75, + 0x73, 0x68, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, + 0x3e, 0x20, 0x24, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x27, 0x73, 0x20, 0x60, 0x6f, 0x70, 0x65, + 0x6e, 0x60, 0x20, 0x69, 0x73, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x64, + 0x69, 0x63, 0x3a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x69, 0x73, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6d, 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x4f, + 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x20, 0x69, 0x73, 0x20, 0x73, 0x65, + 0x74, 0x2e, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x69, 0x6e, 0x67, + 0x20, 0x69, 0x74, 0x20, 0x61, 0x73, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x69, 0x78, 0x65, 0x64, 0x20, 0x74, 0x68, + 0x69, 0x72, 0x64, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, + 0x69, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, 0x6f, 0x64, + 0x65, 0x20, 0x30, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x72, 0x77, 0x69, + 0x6e, 0x20, 0x2d, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x68, + 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x64, + 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x66, 0x6f, 0x72, + 0x2d, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, + 0x61, 0x69, 0x64, 0x20, 0x45, 0x41, 0x43, 0x43, 0x45, 0x53, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x6f, 0x70, + 0x65, 0x6e, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x70, 0x61, 0x74, + 0x68, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x6c, + 0x61, 0x67, 0x73, 0x2c, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x64, 0x69, + 0x63, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x20, 0x24, 0x61, 0x72, 0x67, 0x73, + 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, + 0x66, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, + 0x5f, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x20, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, + 0x24, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x20, 0x24, 0x77, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x29, 0x20, + 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x6e, + 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x75, 0x6e, + 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x70, + 0x61, 0x74, 0x68, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x73, + 0x20, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x70, 0x74, + 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x20, 0x24, 0x66, 0x72, 0x6f, 0x6d, 0x2c, 0x20, 0x70, 0x74, + 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x20, 0x24, 0x74, 0x6f, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x28, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x70, 0x61, 0x74, 0x68, 0x2c, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x61, 0x6d, 0x6f, 0x64, + 0x65, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x61, 0x73, + 0x20, 0x63, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, 0x6f, 0x64, 0x65, 0x29, + 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, + 0x3e, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, + 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, + 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x2f, 0x2f, + 0x20, 0x55, 0x43, 0x52, 0x54, 0x3a, 0x20, 0x60, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x60, 0x2f, 0x60, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x60, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x60, 0x69, 0x6e, 0x74, 0x60, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x60, 0x75, + 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x60, + 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x20, 0x77, 0x72, 0x61, + 0x70, 0x70, 0x69, 0x6e, 0x67, 0x0a, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, + 0x6d, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x72, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x60, 0x69, + 0x73, 0x69, 0x7a, 0x65, 0x60, 0x2f, 0x60, 0x75, 0x73, 0x69, 0x7a, 0x65, + 0x60, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, 0x79, 0x20, 0x50, 0x4f, + 0x53, 0x49, 0x58, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x2e, 0x0a, 0x2f, 0x2f, + 0x20, 0x60, 0x4f, 0x5f, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x60, 0x20, + 0x69, 0x73, 0x20, 0x6f, 0x72, 0x27, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x60, + 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x6f, 0x66, 0x60, 0x20, 0x2d, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x77, 0x6f, + 0x75, 0x6c, 0x64, 0x20, 0x65, 0x61, 0x74, 0x20, 0x60, 0x5c, 0x6e, 0x60, + 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x5f, 0x72, 0x61, 0x77, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, - 0x75, 0x66, 0x2c, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x73, 0x69, 0x7a, - 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x61, 0x73, 0x20, - 0x63, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, - 0x20, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, - 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x75, - 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, - 0x20, 0x3a, 0x20, 0x69, 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x60, 0x66, 0x66, 0x6c, 0x75, 0x73, - 0x68, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x60, 0x20, 0x66, 0x6c, 0x75, - 0x73, 0x68, 0x65, 0x73, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x73, - 0x74, 0x64, 0x69, 0x6f, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2c, - 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, - 0x6e, 0x65, 0x65, 0x64, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x73, 0x74, 0x64, 0x69, 0x6f, 0x3a, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x65, 0x63, 0x68, 0x6f, 0x60, 0x20, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x77, 0x72, - 0x69, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, - 0x20, 0x60, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x60, 0x2c, 0x20, 0x61, - 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x20, 0x69, 0x73, - 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, - 0x66, 0x6c, 0x75, 0x73, 0x68, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x66, - 0x66, 0x6c, 0x75, 0x73, 0x68, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, - 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x27, 0x73, 0x20, 0x60, - 0x6f, 0x70, 0x65, 0x6e, 0x60, 0x20, 0x69, 0x73, 0x20, 0x76, 0x61, 0x72, - 0x69, 0x61, 0x64, 0x69, 0x63, 0x3a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, - 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, - 0x6e, 0x20, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x20, 0x69, 0x73, - 0x20, 0x73, 0x65, 0x74, 0x2e, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, - 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x20, 0x61, 0x73, 0x20, 0x61, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x69, 0x78, 0x65, 0x64, - 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, - 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x30, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x61, - 0x72, 0x77, 0x69, 0x6e, 0x20, 0x2d, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, - 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x66, 0x64, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x64, 0x2c, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x2d, - 0x66, 0x6f, 0x72, 0x2d, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, - 0x6e, 0x20, 0x73, 0x61, 0x69, 0x64, 0x20, 0x45, 0x41, 0x43, 0x43, 0x45, - 0x53, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x63, - 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, - 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, - 0x24, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2c, 0x20, 0x76, 0x61, 0x72, 0x69, - 0x61, 0x64, 0x69, 0x63, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x20, 0x24, 0x61, - 0x72, 0x67, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x20, 0x61, 0x73, 0x20, - 0x63, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x33, - 0x32, 0x20, 0x24, 0x66, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, + 0x75, 0x66, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x20, 0x61, - 0x73, 0x20, 0x63, 0x5f, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, - 0x36, 0x34, 0x20, 0x24, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x77, 0x68, 0x65, 0x6e, 0x63, - 0x65, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, - 0x5f, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, - 0x20, 0x24, 0x70, 0x61, 0x74, 0x68, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x20, 0x61, + 0x73, 0x20, 0x63, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x72, 0x61, 0x77, + 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, 0x2c, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, + 0x62, 0x75, 0x66, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x28, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x66, 0x72, 0x6f, 0x6d, 0x2c, - 0x20, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x74, 0x6f, 0x29, 0x20, 0x3a, - 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x63, 0x63, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x66, 0x6c, 0x75, 0x73, 0x68, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x66, 0x66, 0x6c, 0x75, 0x73, 0x68, + 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, + 0x24, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x29, 0x20, 0x3a, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x6f, 0x70, 0x65, 0x6e, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x70, + 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2c, + 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x64, 0x69, 0x63, 0x5f, 0x61, 0x72, + 0x67, 0x73, 0x20, 0x24, 0x61, 0x72, 0x67, 0x73, 0x29, 0x20, 0x3a, 0x20, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x63, 0x6c, 0x6f, 0x73, + 0x65, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, 0x29, + 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, + 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x69, 0x36, 0x34, 0x20, 0x61, 0x73, 0x20, + 0x63, 0x5f, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, + 0x20, 0x24, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x24, 0x77, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x29, + 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, + 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, + 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, + 0x24, 0x70, 0x61, 0x74, 0x68, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x61, 0x73, 0x20, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, + 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x66, 0x72, 0x6f, 0x6d, 0x2c, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x74, 0x6f, 0x29, 0x20, 0x3a, 0x20, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x70, 0x61, @@ -35916,81 +36553,220 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, - 0x6e, 0x74, 0x38, 0x3e, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, - 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, - 0x68, 0x72, 0x65, 0x61, 0x64, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, - 0x62, 0x65, 0x68, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x27, - 0x73, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x20, - 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x2f, 0x2f, 0x20, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x20, 0x61, 0x74, 0x20, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, - 0x74, 0x65, 0x6c, 0x79, 0x20, 0x2d, 0x20, 0x6e, 0x6f, 0x74, 0x68, 0x69, - 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, 0x6d, 0x61, - 0x79, 0x20, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x20, 0x6c, 0x69, 0x62, 0x63, - 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, - 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x64, 0x61, 0x72, 0x77, 0x69, - 0x6e, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, - 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x65, - 0x72, 0x72, 0x6e, 0x6f, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x3e, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6c, - 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x6c, 0x69, - 0x6e, 0x75, 0x78, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x5f, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x5f, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x65, 0x72, - 0x72, 0x6e, 0x6f, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x28, 0x29, 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x3e, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x73, - 0x65, 0x5d, 0x0a, 0x2f, 0x2f, 0x20, 0x73, 0x6f, 0x20, 0x60, 0x2d, 0x2d, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2d, 0x6f, 0x73, 0x20, 0x77, 0x69, - 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x60, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x73, 0x74, 0x64, 0x6c, 0x69, 0x62, 0x2e, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x61, - 0x72, 0x65, 0x20, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x27, 0x73, 0x3b, 0x20, - 0x6e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, - 0x0a, 0x2f, 0x2f, 0x20, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x20, 0x74, - 0x6f, 0x20, 0x72, 0x75, 0x6e, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x5f, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x5f, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x65, - 0x72, 0x72, 0x6e, 0x6f, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x3e, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6e, - 0x64, 0x5d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x60, 0x4f, 0x5f, 0x2a, 0x60, - 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, - 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, - 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x44, - 0x61, 0x72, 0x77, 0x69, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4c, 0x69, - 0x6e, 0x75, 0x78, 0x2e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, - 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x74, - 0x68, 0x65, 0x73, 0x65, 0x0a, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6e, 0x65, - 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x74, 0x65, - 0x72, 0x61, 0x6c, 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x61, 0x20, 0x66, - 0x6c, 0x61, 0x67, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x68, 0x6f, - 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x2d, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x70, - 0x72, 0x6f, 0x62, 0x6c, 0x65, 0x6d, 0x0a, 0x2f, 0x2f, 0x20, 0x61, 0x74, - 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x69, 0x6d, - 0x65, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, - 0x6e, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x77, - 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x23, 0x5b, - 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x64, 0x61, - 0x72, 0x77, 0x69, 0x6e, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x20, + 0x6e, 0x74, 0x38, 0x3e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x65, 0x72, 0x72, 0x6e, + 0x6f, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, + 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, + 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x64, + 0x20, 0xe2, 0x86, 0x92, 0x20, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45, 0x2e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, + 0x20, 0x41, 0x50, 0x49, 0x73, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x20, + 0x74, 0x61, 0x6b, 0x65, 0x20, 0x61, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x60, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x60, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x54, 0x46, 0x2d, 0x38, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x6f, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x69, + 0x73, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x61, 0x73, + 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x28, 0x38, 0x35, 0x30, 0x2c, 0x20, 0x34, + 0x33, 0x37, 0x29, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, + 0x73, 0x20, 0x77, 0x68, 0x79, 0x20, 0x60, 0x63, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x60, 0x20, 0x67, 0x6f, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, + 0x60, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x60, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x2e, 0x20, 0x70, + 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x4a, 0x49, 0x54, 0x20, 0x6e, 0x65, 0x78, 0x74, 0x20, 0x74, 0x6f, + 0x20, 0x60, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x60, 0x20, 0x66, 0x6f, + 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x73, 0x76, 0x63, 0x72, 0x74, + 0x2d, 0x76, 0x73, 0x2d, 0x55, 0x43, 0x52, 0x54, 0x20, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x6f, 0x73, + 0x66, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, + 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x6f, 0x73, 0x66, 0x68, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, + 0x29, 0x20, 0x3a, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, + 0x4d, 0x6f, 0x64, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, + 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, + 0x24, 0x68, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x3e, 0x20, 0x24, 0x6d, 0x6f, 0x64, 0x65, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x20, + 0x61, 0x73, 0x20, 0x63, 0x5f, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x73, + 0x6f, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x68, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x6d, 0x6f, 0x64, 0x65, 0x29, + 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x57, + 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x57, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, + 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x57, 0x28, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x68, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x31, 0x36, 0x3e, 0x20, 0x24, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x24, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x3e, 0x20, 0x24, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x72, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4d, 0x75, 0x6c, + 0x74, 0x69, 0x42, 0x79, 0x74, 0x65, 0x54, 0x6f, 0x57, 0x69, 0x64, 0x65, + 0x43, 0x68, 0x61, 0x72, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x4d, 0x75, + 0x6c, 0x74, 0x69, 0x42, 0x79, 0x74, 0x65, 0x54, 0x6f, 0x57, 0x69, 0x64, + 0x65, 0x43, 0x68, 0x61, 0x72, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, + 0x70, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, + 0x72, 0x3c, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x20, 0x24, 0x75, 0x74, 0x66, 0x38, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x24, 0x75, 0x74, 0x66, 0x38, 0x5f, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x31, 0x36, 0x3e, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x24, 0x77, 0x69, 0x64, 0x65, 0x5f, 0x6e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x60, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x60, 0x2e, 0x20, 0x60, 0x63, + 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x60, 0x20, 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x20, 0x69, 0x74, 0x0a, 0x2f, 0x2f, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x64, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x20, + 0x77, 0x68, 0x65, 0x6e, 0x20, 0x60, 0x24, 0x66, 0x64, 0x60, 0x20, 0x69, + 0x73, 0x20, 0x61, 0x20, 0x70, 0x69, 0x70, 0x65, 0x20, 0x6f, 0x72, 0x20, + 0x61, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x60, + 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x60, 0x20, 0x73, 0x68, 0x6f, 0x75, + 0x6c, 0x64, 0x20, 0x72, 0x75, 0x6e, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x63, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, + 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x69, + 0x73, 0x69, 0x7a, 0x65, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x6e, 0x20, 0x3d, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, + 0x24, 0x66, 0x64, 0x2c, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x24, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x6e, 0x20, 0x3e, 0x3d, 0x20, 0x30, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x6e, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x5f, 0x72, 0x61, 0x77, 0x28, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x24, 0x62, + 0x75, 0x66, 0x2c, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x61, + 0x73, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x29, 0x20, 0x61, 0x73, + 0x20, 0x69, 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x70, + 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x62, + 0x75, 0x66, 0x2c, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x73, 0x69, 0x7a, + 0x65, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x63, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x72, 0x61, + 0x77, 0x28, 0x24, 0x66, 0x64, 0x2c, 0x20, 0x24, 0x62, 0x75, 0x66, 0x2c, + 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x61, 0x73, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x29, 0x20, 0x61, 0x73, 0x20, 0x69, 0x73, + 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, + 0x5d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x20, + 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2d, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x65, 0x68, 0x69, 0x6e, 0x64, + 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6c, 0x61, 0x74, + 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x0a, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x74, + 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x74, 0x20, 0x69, + 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x20, 0x2d, + 0x20, 0x6e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x72, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x75, 0x63, + 0x68, 0x20, 0x6c, 0x69, 0x62, 0x63, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, + 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, + 0x20, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x5d, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, + 0x61, 0x73, 0x20, 0x63, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x5f, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x3a, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, 0x3b, 0x0a, + 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, + 0x20, 0x3d, 0x3d, 0x20, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x5d, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, 0x65, 0x72, 0x72, 0x6e, + 0x6f, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, + 0x73, 0x20, 0x63, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x5f, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x70, + 0x74, 0x72, 0x3c, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, 0x3b, 0x0a, 0x7d, + 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, + 0x60, 0x4f, 0x5f, 0x2a, 0x60, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x2c, 0x20, + 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x57, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, + 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x0a, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x6c, 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x61, + 0x20, 0x66, 0x6c, 0x61, 0x67, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x6f, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, + 0x68, 0x6f, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6e, 0x67, 0x2d, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0x0a, 0x2f, 0x2f, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x6c, 0x65, 0x6d, 0x20, + 0x61, 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x20, 0x74, + 0x69, 0x6d, 0x65, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x61, 0x6e, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x74, + 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x0a, + 0x23, 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, + 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x4f, 0x4e, 0x4c, + 0x59, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x57, 0x52, 0x4f, 0x4e, 0x4c, 0x59, + 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x20, 0x3d, 0x20, + 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x4f, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, 0x3d, 0x20, 0x38, + 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, + 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x20, 0x3d, 0x20, 0x35, 0x31, 0x32, + 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, + 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x32, + 0x34, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x4f, 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, 0x20, 0x3d, 0x20, + 0x31, 0x36, 0x37, 0x37, 0x37, 0x32, 0x31, 0x36, 0x3b, 0x0a, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x42, 0x49, 0x4e, + 0x41, 0x52, 0x59, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x23, 0x5b, 0x65, + 0x6c, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x6c, + 0x69, 0x6e, 0x75, 0x78, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x57, 0x52, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x3d, @@ -35999,19 +36775,22 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x20, 0x3d, 0x20, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, - 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, 0x3d, 0x20, 0x38, 0x3b, 0x0a, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x43, - 0x52, 0x45, 0x41, 0x54, 0x20, 0x3d, 0x20, 0x35, 0x31, 0x32, 0x3b, 0x0a, + 0x43, 0x52, 0x45, 0x41, 0x54, 0x20, 0x3d, 0x20, 0x36, 0x34, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x54, - 0x52, 0x55, 0x4e, 0x43, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x32, 0x34, 0x3b, - 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, - 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, 0x20, 0x3d, 0x20, 0x31, 0x36, - 0x37, 0x37, 0x37, 0x32, 0x31, 0x36, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, - 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x6c, 0x69, - 0x6e, 0x75, 0x78, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x52, 0x55, 0x4e, 0x43, 0x20, 0x3d, 0x20, 0x35, 0x31, 0x32, 0x3b, 0x0a, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x41, + 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x32, 0x34, + 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, + 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, 0x20, 0x3d, 0x20, 0x35, + 0x32, 0x34, 0x32, 0x38, 0x38, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x69, 0x66, + 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, @@ -36021,108 +36800,114 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x20, 0x3d, 0x20, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x43, - 0x52, 0x45, 0x41, 0x54, 0x20, 0x3d, 0x20, 0x36, 0x34, 0x3b, 0x0a, 0x69, + 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x41, + 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, 0x3d, 0x20, 0x38, 0x3b, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x43, 0x52, + 0x45, 0x41, 0x54, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x36, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x20, 0x3d, 0x20, 0x35, 0x31, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x41, 0x50, - 0x50, 0x45, 0x4e, 0x44, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x32, 0x34, 0x3b, - 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, - 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, 0x20, 0x3d, 0x20, 0x35, 0x32, - 0x34, 0x32, 0x38, 0x38, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x73, 0x65, - 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, - 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, - 0x5f, 0x52, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x3d, 0x20, 0x30, 0x3b, - 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, - 0x57, 0x52, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, - 0x44, 0x57, 0x52, 0x20, 0x3d, 0x20, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, - 0x54, 0x20, 0x3d, 0x20, 0x36, 0x34, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, - 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, - 0x20, 0x3d, 0x20, 0x35, 0x31, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, - 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, - 0x44, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x32, 0x34, 0x3b, 0x0a, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x43, 0x4c, 0x4f, - 0x45, 0x58, 0x45, 0x43, 0x20, 0x3d, 0x20, 0x35, 0x32, 0x34, 0x32, 0x38, - 0x38, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x2f, - 0x2f, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x66, 0x6f, 0x75, 0x72, - 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x45, 0x42, 0x41, 0x44, 0x46, 0x2c, - 0x20, 0x61, 0x67, 0x72, 0x65, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x65, 0x76, - 0x65, 0x72, 0x79, 0x20, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, - 0x65, 0x72, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x0a, 0x69, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x43, 0x4c, + 0x4f, 0x45, 0x58, 0x45, 0x43, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x50, 0x45, 0x52, - 0x4d, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x20, 0x45, 0x4e, 0x4f, 0x45, 0x4e, 0x54, 0x20, 0x3d, - 0x20, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, - 0x20, 0x45, 0x42, 0x41, 0x44, 0x46, 0x20, 0x3d, 0x20, 0x39, 0x3b, 0x0a, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x41, 0x43, - 0x43, 0x45, 0x53, 0x20, 0x3d, 0x20, 0x31, 0x33, 0x3b, 0x0a, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x42, 0x49, + 0x4e, 0x41, 0x52, 0x59, 0x20, 0x3d, 0x20, 0x33, 0x32, 0x37, 0x36, 0x38, + 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x73, 0x65, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x45, 0x58, 0x49, 0x53, - 0x54, 0x20, 0x3d, 0x20, 0x31, 0x37, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x4f, + 0x4e, 0x4c, 0x59, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, - 0x45, 0x54, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x57, 0x52, 0x4f, 0x4e, + 0x4c, 0x59, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, - 0x6e, 0x74, 0x33, 0x32, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x43, 0x55, - 0x52, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x45, 0x4e, 0x44, - 0x20, 0x3d, 0x20, 0x32, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x20, + 0x3d, 0x20, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x20, 0x3d, 0x20, + 0x36, 0x34, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x20, 0x3d, 0x20, 0x35, + 0x31, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x20, 0x4f, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, 0x3d, 0x20, + 0x31, 0x30, 0x32, 0x34, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x43, 0x4c, 0x4f, 0x45, 0x58, 0x45, 0x43, + 0x20, 0x3d, 0x20, 0x35, 0x32, 0x34, 0x32, 0x38, 0x38, 0x3b, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, 0x5f, 0x42, 0x49, + 0x4e, 0x41, 0x52, 0x59, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x23, 0x5b, + 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, + 0x73, 0x65, 0x20, 0x66, 0x6f, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x45, 0x42, 0x41, 0x44, 0x46, 0x2c, 0x20, 0x61, 0x67, 0x72, 0x65, + 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x70, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x73, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x20, 0x45, 0x50, 0x45, 0x52, 0x4d, 0x20, 0x3d, 0x20, 0x31, + 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, + 0x4e, 0x4f, 0x45, 0x4e, 0x54, 0x20, 0x3d, 0x20, 0x32, 0x3b, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x42, 0x41, 0x44, + 0x46, 0x20, 0x3d, 0x20, 0x39, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x20, 0x46, 0x5f, 0x4f, 0x4b, 0x20, 0x3d, 0x20, 0x30, - 0x3b, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x30, 0x36, 0x36, 0x36, 0x2e, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x75, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x74, 0x61, - 0x6b, 0x65, 0x73, 0x20, 0x69, 0x74, 0x73, 0x20, 0x75, 0x73, 0x75, 0x61, - 0x6c, 0x20, 0x63, 0x75, 0x74, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x20, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x20, 0x3d, 0x20, 0x34, 0x33, 0x38, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x49, 0x4f, 0x5f, 0x42, 0x55, - 0x46, 0x20, 0x3d, 0x20, 0x38, 0x31, 0x39, 0x32, 0x3b, 0x0a, 0x0a, 0x2f, - 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x60, 0x65, - 0x72, 0x72, 0x6e, 0x6f, 0x60, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, - 0x73, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x6c, - 0x69, 0x62, 0x63, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x6c, 0x65, 0x66, - 0x74, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x60, 0x69, 0x6f, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x60, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, - 0x2a, 0x20, 0x43, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, - 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x79, 0x74, 0x68, - 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x74, 0x68, 0x61, - 0x74, 0x20, 0x6d, 0x69, 0x67, 0x68, 0x74, 0x20, 0x6f, 0x76, 0x65, 0x72, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x20, 0x2a, - 0x2f, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, - 0x6f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x70, 0x74, 0x72, 0x3c, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, 0x20, - 0x24, 0x73, 0x6c, 0x6f, 0x74, 0x20, 0x3d, 0x20, 0x63, 0x5f, 0x65, 0x72, - 0x72, 0x6e, 0x6f, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x33, - 0x32, 0x20, 0x24, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x24, 0x73, - 0x6c, 0x6f, 0x74, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6f, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x28, 0x24, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, + 0x74, 0x33, 0x32, 0x20, 0x45, 0x41, 0x43, 0x43, 0x45, 0x53, 0x20, 0x3d, + 0x20, 0x31, 0x33, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x45, 0x45, 0x58, 0x49, 0x53, 0x54, 0x20, 0x3d, 0x20, 0x31, + 0x37, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x43, 0x55, 0x52, 0x20, 0x3d, 0x20, 0x31, + 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x53, + 0x45, 0x45, 0x4b, 0x5f, 0x45, 0x4e, 0x44, 0x20, 0x3d, 0x20, 0x32, 0x3b, + 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x46, + 0x5f, 0x4f, 0x4b, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x0a, 0x2f, 0x2f, + 0x20, 0x30, 0x36, 0x36, 0x36, 0x2e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, + 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x20, 0x69, + 0x74, 0x73, 0x20, 0x75, 0x73, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x75, 0x74, + 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4f, + 0x50, 0x45, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x20, 0x3d, 0x20, 0x34, + 0x33, 0x38, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x49, 0x4f, 0x5f, 0x42, 0x55, 0x46, 0x20, 0x3d, 0x20, 0x38, + 0x31, 0x39, 0x32, 0x3b, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, + 0x20, 0x54, 0x68, 0x65, 0x20, 0x60, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x60, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x66, 0x61, + 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x69, 0x62, 0x63, 0x20, 0x63, + 0x61, 0x6c, 0x6c, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x2c, 0x20, 0x61, 0x73, + 0x20, 0x61, 0x6e, 0x20, 0x60, 0x69, 0x6f, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x60, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x43, 0x61, 0x6c, + 0x6c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, + 0x65, 0x20, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x65, + 0x6c, 0x73, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x69, 0x67, + 0x68, 0x74, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6f, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, 0x20, 0x24, 0x73, 0x6c, 0x6f, 0x74, + 0x20, 0x3d, 0x20, 0x63, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x5f, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, 0x6f, + 0x64, 0x65, 0x20, 0x3d, 0x20, 0x24, 0x73, 0x6c, 0x6f, 0x74, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x69, 0x6f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x24, 0x63, 0x6f, 0x64, + 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; - file_36.set_content(reinterpret_cast(file_36_data), sizeof(file_36_data)); - auto &file_37 = module.add_file("stdlib:/std/io/print.eco"); - static const unsigned char file_37_data[] = { + file_37.set_content(reinterpret_cast(file_37_data), sizeof(file_37_data)); + auto &file_38 = module.add_file("stdlib:/std/io/print.eco"); + static const unsigned char file_38_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x72, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x46, 0x6f, 0x75, 0x72, 0x20, @@ -36251,9 +37036,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x24, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; - file_37.set_content(reinterpret_cast(file_37_data), sizeof(file_37_data)); - auto &file_38 = module.add_file("stdlib:/std/io/read.eco"); - static const unsigned char file_38_data[] = { + file_38.set_content(reinterpret_cast(file_38_data), sizeof(file_38_data)); + auto &file_39 = module.add_file("stdlib:/std/io/read.eco"); + static const unsigned char file_39_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x69, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, @@ -36310,9 +37095,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x64, 0x69, 0x6e, 0x2d, 0x3e, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; - file_38.set_content(reinterpret_cast(file_38_data), sizeof(file_38_data)); - auto &file_39 = module.add_file("stdlib:/std/io/reader.eco"); - static const unsigned char file_39_data[] = { + file_39.set_content(reinterpret_cast(file_39_data), sizeof(file_39_data)); + auto &file_40 = module.add_file("stdlib:/std/io/reader.eco"); + static const unsigned char file_40_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x61, 0x20, 0x60, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x60, @@ -36390,9 +37175,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x3e, 0x72, 0x69, 0x6e, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, }; - file_39.set_content(reinterpret_cast(file_39_data), sizeof(file_39_data)); - auto &file_40 = module.add_file("stdlib:/std/io/stream.eco"); - static const unsigned char file_40_data[] = { + file_40.set_content(reinterpret_cast(file_40_data), sizeof(file_40_data)); + auto &file_41 = module.add_file("stdlib:/std/io/stream.eco"); + static const unsigned char file_41_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x69, 0x74, 0x73, 0x20, 0x66, @@ -36862,9 +37647,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x74, 0x64, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x28, 0x30, 0x29, 0x3b, 0x0a, }; - file_40.set_content(reinterpret_cast(file_40_data), sizeof(file_40_data)); - auto &file_41 = module.add_file("stdlib:/std/io/writer.eco"); - static const unsigned char file_41_data[] = { + file_41.set_content(reinterpret_cast(file_41_data), sizeof(file_41_data)); + auto &file_42 = module.add_file("stdlib:/std/io/writer.eco"); + static const unsigned char file_42_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x61, 0x20, 0x60, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, @@ -37011,9 +37796,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x2e, 0x6f, 0x6b, 0x28, 0x24, 0x6e, 0x20, 0x2b, 0x20, 0x24, 0x6e, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, }; - file_41.set_content(reinterpret_cast(file_41_data), sizeof(file_41_data)); - auto &file_42 = module.add_file("stdlib:/std/math/constants.eco"); - static const unsigned char file_42_data[] = { + file_42.set_content(reinterpret_cast(file_42_data), sizeof(file_42_data)); + auto &file_43 = module.add_file("stdlib:/std/math/constants.eco"); + static const unsigned char file_43_data[] = { 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x6d, 0x61, 0x74, 0x68, 0x3b, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x74, @@ -37205,11 +37990,96 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x53, 0x51, 0x52, 0x54, 0x5f, 0x32, 0x5f, 0x46, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x34, - 0x31, 0x34, 0x32, 0x31, 0x33, 0x35, 0x66, 0x3b, 0x0a, + 0x31, 0x34, 0x32, 0x31, 0x33, 0x35, 0x66, 0x3b, 0x0a, 0x0a, 0x2f, 0x2f, + 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x67, 0x65, 0x72, 0x20, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x61, + 0x0a, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x2f, 0x2a, + 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x72, + 0x67, 0x65, 0x73, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, + 0x66, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x65, 0x72, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2e, 0x20, 0x60, 0x4d, + 0x49, 0x4e, 0x5f, 0x2a, 0x60, 0x20, 0x69, 0x73, 0x20, 0x77, 0x72, 0x69, + 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x60, 0x2d, 0x4d, 0x41, + 0x58, 0x20, 0x2d, 0x20, 0x31, 0x60, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x0a, 0x20, 0x2a, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x74, 0x75, + 0x64, 0x65, 0x20, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x68, 0x61, 0x73, + 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x68, 0x6f, + 0x6c, 0x64, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x38, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x54, 0x38, 0x20, 0x3d, + 0x20, 0x31, 0x32, 0x37, 0x3b, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x38, 0x20, + 0x4d, 0x49, 0x4e, 0x5f, 0x49, 0x4e, 0x54, 0x38, 0x20, 0x3d, 0x20, 0x2d, + 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x54, 0x38, 0x20, 0x2d, 0x20, 0x31, + 0x3b, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x20, 0x4d, 0x41, 0x58, + 0x5f, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x20, 0x3d, 0x20, 0x33, 0x32, 0x37, + 0x36, 0x37, 0x3b, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x20, 0x4d, + 0x49, 0x4e, 0x5f, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x20, 0x3d, 0x20, 0x2d, + 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x20, 0x2d, 0x20, + 0x31, 0x3b, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4d, 0x41, + 0x58, 0x5f, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x32, 0x31, + 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x37, 0x3b, 0x0a, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4d, 0x49, 0x4e, 0x5f, 0x49, 0x4e, 0x54, + 0x33, 0x32, 0x20, 0x3d, 0x20, 0x2d, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, + 0x54, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x0a, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x54, 0x36, + 0x34, 0x20, 0x3d, 0x20, 0x39, 0x32, 0x32, 0x33, 0x33, 0x37, 0x32, 0x30, + 0x33, 0x36, 0x38, 0x35, 0x34, 0x37, 0x37, 0x35, 0x38, 0x30, 0x37, 0x3b, + 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x4d, 0x49, 0x4e, 0x5f, + 0x49, 0x4e, 0x54, 0x36, 0x34, 0x20, 0x3d, 0x20, 0x2d, 0x4d, 0x41, 0x58, + 0x5f, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x0a, + 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x20, 0x4d, 0x41, 0x58, 0x5f, + 0x55, 0x49, 0x4e, 0x54, 0x38, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x35, 0x3b, + 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x20, 0x4d, 0x41, 0x58, + 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x20, 0x3d, 0x20, 0x36, 0x35, + 0x35, 0x33, 0x35, 0x3b, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x20, + 0x3d, 0x20, 0x34, 0x32, 0x39, 0x34, 0x39, 0x36, 0x37, 0x32, 0x39, 0x35, + 0x3b, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x4d, 0x41, + 0x58, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x20, 0x3d, 0x20, 0x31, + 0x38, 0x34, 0x34, 0x36, 0x37, 0x34, 0x34, 0x30, 0x37, 0x33, 0x37, 0x30, + 0x39, 0x35, 0x35, 0x31, 0x36, 0x31, 0x35, 0x3b, 0x0a, 0x0a, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, + 0x73, 0x69, 0x7a, 0x65, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x53, 0x49, + 0x5a, 0x45, 0x20, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x54, + 0x36, 0x34, 0x20, 0x61, 0x73, 0x20, 0x69, 0x73, 0x69, 0x7a, 0x65, 0x3b, + 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x69, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x4d, 0x49, 0x4e, 0x5f, + 0x49, 0x53, 0x49, 0x5a, 0x45, 0x20, 0x3d, 0x20, 0x2d, 0x4d, 0x41, 0x58, + 0x5f, 0x49, 0x53, 0x49, 0x5a, 0x45, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x0a, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x55, + 0x53, 0x49, 0x5a, 0x45, 0x20, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x55, + 0x49, 0x4e, 0x54, 0x36, 0x34, 0x20, 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, + 0x7a, 0x65, 0x3b, 0x0a, }; - file_42.set_content(reinterpret_cast(file_42_data), sizeof(file_42_data)); - auto &file_43 = module.add_file("stdlib:/std/math/functions.eco"); - static const unsigned char file_43_data[] = { + file_43.set_content(reinterpret_cast(file_43_data), sizeof(file_43_data)); + auto &file_44 = module.add_file("stdlib:/std/math/functions.eco"); + static const unsigned char file_44_data[] = { 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x6d, 0x61, 0x74, 0x68, 0x3b, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, @@ -37292,9 +38162,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x2c, 0x20, 0x24, 0x6d, 0x69, 0x6e, 0x29, 0x2c, 0x20, 0x24, 0x6d, 0x61, 0x78, 0x29, 0x3b, 0x0a, 0x7d, }; - file_43.set_content(reinterpret_cast(file_43_data), sizeof(file_43_data)); - auto &file_44 = module.add_file("stdlib:/std/math/intrinsics.eco"); - static const unsigned char file_44_data[] = { + file_44.set_content(reinterpret_cast(file_44_data), sizeof(file_44_data)); + auto &file_45 = module.add_file("stdlib:/std/math/intrinsics.eco"); + static const unsigned char file_45_data[] = { 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x6d, 0x61, 0x74, 0x68, 0x3b, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x20, 0x66, 0x75, @@ -38082,9 +38952,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x20, 0x24, 0x63, 0x29, 0x20, 0x3a, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x36, 0x34, 0x3b, 0x0a, }; - file_44.set_content(reinterpret_cast(file_44_data), sizeof(file_44_data)); - auto &file_45 = module.add_file("stdlib:/std/thread/channel.eco"); - static const unsigned char file_45_data[] = { + file_45.set_content(reinterpret_cast(file_45_data), sizeof(file_45_data)); + auto &file_46 = module.add_file("stdlib:/std/thread/channel.eco"); + static const unsigned char file_46_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x71, 0x75, 0x65, 0x75, 0x65, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x2e, 0x20, 0x60, 0x73, 0x65, 0x6e, @@ -38615,9 +39485,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2e, 0x6f, 0x6b, 0x28, 0x24, 0x72, 0x78, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; - file_45.set_content(reinterpret_cast(file_45_data), sizeof(file_45_data)); - auto &file_46 = module.add_file("stdlib:/std/thread/error.eco"); - static const unsigned char file_46_data[] = { + file_46.set_content(reinterpret_cast(file_46_data), sizeof(file_46_data)); + auto &file_47 = module.add_file("stdlib:/std/thread/error.eco"); + static const unsigned char file_47_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x68, 0x79, 0x20, 0x61, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x74, 0x61, @@ -38653,9 +39523,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x3d, 0x3d, 0x20, 0x45, 0x41, 0x47, 0x41, 0x49, 0x4e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, }; - file_46.set_content(reinterpret_cast(file_46_data), sizeof(file_46_data)); - auto &file_47 = module.add_file("stdlib:/std/thread/libc.eco"); - static const unsigned char file_47_data[] = { + file_47.set_content(reinterpret_cast(file_47_data), sizeof(file_47_data)); + auto &file_48 = module.add_file("stdlib:/std/thread/libc.eco"); + static const unsigned char file_48_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x45, 0x76, 0x65, 0x72, 0x79, 0x20, 0x43, 0x20, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, @@ -38697,187 +39567,554 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x69, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, - 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, - 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x70, 0x74, 0x72, 0x3c, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x3e, 0x20, 0x24, - 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, - 0x38, 0x3e, 0x20, 0x24, 0x61, 0x74, 0x74, 0x72, 0x2c, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x70, 0x74, - 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x70, 0x74, 0x72, - 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x29, 0x3e, 0x20, 0x24, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, - 0x20, 0x24, 0x61, 0x72, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, - 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, - 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x20, - 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, - 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, - 0x24, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, - 0x3c, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x3e, - 0x20, 0x24, 0x72, 0x65, 0x74, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x3a, 0x20, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x3b, 0x0a, 0x0a, 0x23, 0x5b, 0x69, + 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x3e, 0x20, 0x24, 0x74, 0x68, 0x72, + 0x65, 0x61, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, + 0x24, 0x61, 0x74, 0x74, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x70, 0x74, 0x72, 0x3c, 0x75, + 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x29, 0x3e, 0x20, 0x24, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, + 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x61, + 0x72, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x3a, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, - 0x65, 0x61, 0x64, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x20, 0x61, + 0x65, 0x61, 0x64, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x20, 0x61, 0x73, 0x20, + 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6a, 0x6f, + 0x69, 0x6e, 0x28, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x70, 0x74, + 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x3e, 0x20, 0x24, 0x72, + 0x65, 0x74, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x20, 0x61, 0x73, 0x20, 0x63, + 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x64, 0x65, 0x74, + 0x61, 0x63, 0x68, 0x28, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x28, + 0x29, 0x20, 0x3a, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x65, 0x71, 0x75, 0x61, + 0x6c, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x28, 0x75, 0x73, 0x69, + 0x7a, 0x65, 0x20, 0x24, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, + 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, - 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x28, 0x75, 0x73, 0x69, 0x7a, 0x65, - 0x20, 0x24, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x29, 0x20, 0x3a, 0x20, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, - 0x65, 0x61, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x61, 0x73, 0x20, - 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x73, 0x65, - 0x6c, 0x66, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x65, - 0x71, 0x75, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, - 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x28, - 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x61, 0x2c, 0x20, 0x75, 0x73, - 0x69, 0x7a, 0x65, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x69, - 0x74, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x69, - 0x74, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, - 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x70, 0x74, 0x72, - 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x61, 0x74, 0x74, - 0x72, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, - 0x65, 0x78, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x20, 0x61, + 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x28, 0x70, + 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, + 0x75, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x61, 0x74, 0x74, 0x72, 0x29, 0x20, + 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, + 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x20, 0x61, 0x73, 0x20, 0x63, + 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, + 0x65, 0x78, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x28, 0x70, + 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, + 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, - 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, - 0x79, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, - 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, - 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x6c, 0x6f, 0x63, + 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x28, 0x70, + 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, + 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x74, 0x72, 0x79, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x6c, 0x6f, 0x63, - 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, - 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, - 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x74, 0x72, 0x79, - 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, + 0x6c, 0x6f, 0x63, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, + 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, - 0x74, 0x72, 0x79, 0x6c, 0x6f, 0x63, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, + 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, + 0x78, 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x28, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x75, 0x74, + 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, + 0x6f, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x20, 0x61, 0x73, 0x20, + 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, + 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x28, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, + 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x20, 0x24, 0x61, 0x74, 0x74, 0x72, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x72, + 0x6f, 0x79, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, + 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x73, + 0x74, 0x72, 0x6f, 0x79, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x77, 0x61, + 0x69, 0x74, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, + 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x77, 0x61, 0x69, + 0x74, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, - 0x65, 0x78, 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x61, 0x73, - 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, - 0x75, 0x74, 0x65, 0x78, 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x28, + 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, + 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x20, + 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, + 0x6e, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x28, 0x70, 0x74, + 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, + 0x6e, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, + 0x6e, 0x64, 0x5f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x62, 0x72, 0x6f, 0x61, 0x64, + 0x63, 0x61, 0x73, 0x74, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x63, + 0x68, 0x65, 0x64, 0x5f, 0x79, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x61, 0x73, + 0x20, 0x63, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x79, 0x69, 0x65, + 0x6c, 0x64, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x73, 0x79, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x61, + 0x73, 0x20, 0x63, 0x5f, 0x73, 0x79, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x28, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x6e, 0x61, 0x6d, 0x65, 0x29, + 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x7d, 0x0a, + 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x23, 0x5b, 0x69, 0x66, + 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x20, 0x24, 0x61, 0x74, 0x74, 0x72, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, + 0x24, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, + 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x29, 0x3e, 0x20, 0x24, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, - 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x20, - 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, - 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x28, 0x70, + 0x61, 0x72, 0x67, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x6c, 0x61, + 0x67, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3e, 0x20, + 0x24, 0x74, 0x69, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x3a, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x53, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x61, + 0x73, 0x20, 0x63, 0x5f, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x53, + 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, + 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x20, 0x24, 0x6d, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x6c, 0x6f, 0x73, 0x65, + 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, + 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x28, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, + 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x49, 0x64, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x49, 0x64, + 0x28, 0x29, 0x20, 0x3a, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, 0x6f, 0x54, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x53, 0x77, + 0x69, 0x74, 0x63, 0x68, 0x54, 0x6f, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x50, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, + 0x20, 0x24, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x29, 0x20, 0x3a, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, + 0x63, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, + 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x20, 0x24, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, 0x6b, 0x45, 0x78, 0x63, 0x6c, 0x75, + 0x73, 0x69, 0x76, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x41, 0x63, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, 0x6b, + 0x45, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x28, 0x70, 0x74, + 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6c, 0x6f, + 0x63, 0x6b, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x53, 0x52, 0x57, 0x4c, + 0x6f, 0x63, 0x6b, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, + 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, 0x6b, 0x45, 0x78, 0x63, 0x6c, + 0x75, 0x73, 0x69, 0x76, 0x65, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x20, + 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x54, 0x72, 0x79, + 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, + 0x63, 0x6b, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x20, + 0x61, 0x73, 0x20, 0x63, 0x5f, 0x54, 0x72, 0x79, 0x41, 0x63, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, 0x6b, 0x45, 0x78, + 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x28, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6c, 0x6f, 0x63, 0x6b, + 0x29, 0x20, 0x3a, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x49, 0x6e, + 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x43, 0x6f, + 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, 0x61, + 0x62, 0x6c, 0x65, 0x53, 0x52, 0x57, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, + 0x53, 0x6c, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x52, + 0x57, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, - 0x74, 0x38, 0x3e, 0x20, 0x24, 0x61, 0x74, 0x74, 0x72, 0x29, 0x20, 0x3a, - 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, - 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x64, 0x65, - 0x73, 0x74, 0x72, 0x6f, 0x79, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, - 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, - 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x28, 0x70, 0x74, 0x72, 0x3c, - 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x6d, 0x73, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, - 0x5f, 0x77, 0x61, 0x69, 0x74, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, - 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, - 0x77, 0x61, 0x69, 0x74, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, - 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x2c, 0x20, 0x70, - 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, - 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, - 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, - 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x20, - 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, - 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, + 0x57, 0x61, 0x6b, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x73, + 0x20, 0x63, 0x5f, 0x57, 0x61, 0x6b, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, - 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, - 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, - 0x61, 0x73, 0x74, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, - 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x62, 0x72, - 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x28, 0x70, 0x74, 0x72, 0x3c, - 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, - 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x73, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x79, 0x69, 0x65, 0x6c, 0x64, - 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x5f, - 0x79, 0x69, 0x65, 0x6c, 0x64, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x6c, 0x65, 0x65, 0x70, - 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x75, 0x73, 0x6c, 0x65, 0x65, 0x70, - 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x75, 0x73, 0x65, - 0x63, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x73, 0x79, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x61, 0x73, 0x20, - 0x63, 0x5f, 0x73, 0x79, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x28, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x20, 0x24, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x3a, - 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x4e, 0x4f, 0x4d, - 0x45, 0x4d, 0x20, 0x3d, 0x20, 0x31, 0x32, 0x3b, 0x0a, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x42, 0x55, 0x53, 0x59, 0x20, - 0x3d, 0x20, 0x31, 0x36, 0x3b, 0x0a, 0x0a, 0x23, 0x5b, 0x69, 0x66, 0x3a, - 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x64, 0x61, 0x72, 0x77, 0x69, - 0x6e, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, - 0x45, 0x41, 0x47, 0x41, 0x49, 0x4e, 0x20, 0x3d, 0x20, 0x33, 0x35, 0x3b, - 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x44, - 0x45, 0x41, 0x44, 0x4c, 0x4b, 0x20, 0x3d, 0x20, 0x31, 0x31, 0x3b, 0x0a, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x53, 0x43, 0x5f, - 0x4e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x53, 0x5f, - 0x4f, 0x4e, 0x4c, 0x4e, 0x20, 0x3d, 0x20, 0x35, 0x38, 0x3b, 0x0a, 0x23, - 0x5b, 0x65, 0x6c, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, - 0x20, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, - 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x41, 0x47, 0x41, 0x49, 0x4e, 0x20, - 0x3d, 0x20, 0x31, 0x31, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x20, 0x45, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x4b, 0x20, 0x3d, - 0x20, 0x33, 0x35, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, - 0x32, 0x20, 0x53, 0x43, 0x5f, 0x4e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, - 0x53, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x4e, 0x4c, 0x4e, 0x20, 0x3d, 0x20, - 0x38, 0x34, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x73, 0x65, 0x5d, 0x0a, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x41, 0x47, - 0x41, 0x49, 0x4e, 0x20, 0x3d, 0x20, 0x31, 0x31, 0x3b, 0x0a, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x44, 0x45, 0x41, 0x44, - 0x4c, 0x4b, 0x20, 0x3d, 0x20, 0x33, 0x35, 0x3b, 0x0a, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x53, 0x43, 0x5f, 0x4e, 0x50, 0x52, - 0x4f, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x4e, 0x4c, - 0x4e, 0x20, 0x3d, 0x20, 0x38, 0x34, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6e, - 0x64, 0x5d, 0x0a, 0x0a, 0x0a, - }; - file_47.set_content(reinterpret_cast(file_47_data), sizeof(file_47_data)); - auto &file_48 = module.add_file("stdlib:/std/thread/lock.eco"); - static const unsigned char file_48_data[] = { - 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x73, 0x6c, 0x65, - 0x65, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x2c, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x75, 0x61, + 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x57, 0x61, 0x6b, 0x65, 0x41, 0x6c, 0x6c, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x57, 0x61, + 0x6b, 0x65, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x70, + 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, + 0x6f, 0x6e, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x57, 0x49, 0x4e, 0x5f, 0x49, 0x4e, 0x46, 0x49, 0x4e, 0x49, + 0x54, 0x45, 0x20, 0x3d, 0x20, 0x34, 0x32, 0x39, 0x34, 0x39, 0x36, 0x37, + 0x32, 0x39, 0x35, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x31, 0x36, 0x20, 0x57, 0x49, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x50, + 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x5f, 0x47, 0x52, 0x4f, + 0x55, 0x50, 0x53, 0x20, 0x3d, 0x20, 0x36, 0x35, 0x35, 0x33, 0x35, 0x3b, + 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x73, + 0x69, 0x7a, 0x65, 0x3e, 0x20, 0x24, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x61, 0x74, 0x74, 0x72, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, + 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x29, 0x3e, 0x20, 0x24, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x61, 0x72, 0x67, 0x0a, + 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, + 0x74, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, + 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x5f, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x24, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, 0x24, 0x61, 0x72, 0x67, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x26, 0x24, 0x74, 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x68, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x3a, 0x24, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, + 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x45, 0x4e, 0x4f, 0x4d, + 0x45, 0x4d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x62, 0x69, + 0x74, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x65, 0x6d, 0x3a, 0x3a, 0x63, 0x6f, 0x70, 0x79, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x28, 0x26, 0x24, 0x62, 0x69, 0x74, 0x73, 0x29, 0x2c, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, + 0x26, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x2c, 0x20, 0x6d, + 0x65, 0x6d, 0x3a, 0x3a, 0x73, 0x69, 0x7a, 0x65, 0x3c, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x3e, 0x28, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x24, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x3a, 0x24, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x24, 0x62, 0x69, 0x74, + 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x3e, 0x20, + 0x24, 0x72, 0x65, 0x74, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x3a, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x62, 0x69, 0x74, 0x73, 0x20, 0x3d, + 0x20, 0x24, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x20, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x70, + 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x6e, 0x75, + 0x6c, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, + 0x3a, 0x3a, 0x63, 0x6f, 0x70, 0x79, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, + 0x3e, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x28, 0x26, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x2c, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, 0x26, + 0x24, 0x62, 0x69, 0x74, 0x73, 0x29, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x3a, + 0x3a, 0x73, 0x69, 0x7a, 0x65, 0x3c, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x3e, 0x28, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x5f, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x53, + 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, + 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x57, 0x49, 0x4e, + 0x5f, 0x49, 0x4e, 0x46, 0x49, 0x4e, 0x49, 0x54, 0x45, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x48, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x28, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x28, 0x75, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x24, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x62, 0x69, 0x74, 0x73, + 0x20, 0x3d, 0x20, 0x24, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x20, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, + 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x28, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x65, 0x6d, 0x3a, 0x3a, 0x63, 0x6f, 0x70, 0x79, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x28, 0x26, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, + 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x28, 0x26, 0x24, 0x62, 0x69, 0x74, 0x73, 0x29, 0x2c, 0x20, 0x6d, 0x65, + 0x6d, 0x3a, 0x3a, 0x73, 0x69, 0x7a, 0x65, 0x3c, 0x70, 0x74, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x3e, 0x28, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x48, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x28, 0x24, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x75, 0x73, + 0x69, 0x7a, 0x65, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x5f, 0x47, 0x65, 0x74, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x49, + 0x64, 0x28, 0x29, 0x20, 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, + 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x65, 0x71, 0x75, + 0x61, 0x6c, 0x28, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x61, 0x2c, + 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x24, + 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x69, + 0x74, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, + 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x61, 0x74, 0x74, + 0x72, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x49, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, 0x6b, + 0x28, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, + 0x5f, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x28, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x75, 0x74, + 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, + 0x75, 0x74, 0x65, 0x78, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x28, 0x70, 0x74, + 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x75, + 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x41, 0x63, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, 0x6b, 0x45, + 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x28, 0x24, 0x6d, 0x75, + 0x74, 0x65, 0x78, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x74, 0x72, 0x79, + 0x6c, 0x6f, 0x63, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, + 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x5f, 0x54, 0x72, 0x79, 0x41, + 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, + 0x6b, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x28, 0x24, + 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x45, 0x42, 0x55, 0x53, 0x59, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x5f, 0x75, + 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, + 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x5f, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x53, 0x52, 0x57, 0x4c, 0x6f, 0x63, 0x6b, 0x45, 0x78, 0x63, 0x6c, 0x75, + 0x73, 0x69, 0x76, 0x65, 0x28, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, + 0x6f, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x28, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, + 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, + 0x3e, 0x20, 0x24, 0x61, 0x74, 0x74, 0x72, 0x29, 0x20, 0x3a, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x5f, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x28, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, + 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x28, 0x70, + 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, + 0x6f, 0x6e, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x28, 0x70, 0x74, + 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, + 0x6e, 0x64, 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, + 0x38, 0x3e, 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x5f, 0x53, 0x6c, 0x65, 0x65, 0x70, + 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x52, 0x57, 0x28, 0x24, 0x63, 0x6f, + 0x6e, 0x64, 0x2c, 0x20, 0x24, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x2c, 0x20, + 0x57, 0x49, 0x4e, 0x5f, 0x49, 0x4e, 0x46, 0x49, 0x4e, 0x49, 0x54, 0x45, + 0x2c, 0x20, 0x30, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x45, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x4b, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x6c, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x75, 0x69, 0x6e, + 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x5f, 0x57, 0x61, 0x6b, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x28, 0x24, 0x63, 0x6f, 0x6e, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, + 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x70, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x62, + 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x28, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x6e, + 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x57, 0x61, 0x6b, 0x65, 0x41, + 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x24, 0x63, 0x6f, 0x6e, + 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x79, + 0x69, 0x65, 0x6c, 0x64, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x53, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, 0x6f, 0x54, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x73, 0x79, 0x73, 0x63, 0x6f, 0x6e, + 0x66, 0x28, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x6e, 0x61, 0x6d, + 0x65, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x63, 0x5f, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x50, + 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x28, 0x57, 0x49, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x50, 0x52, + 0x4f, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x5f, 0x47, 0x52, 0x4f, 0x55, + 0x50, 0x53, 0x29, 0x20, 0x61, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, + 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x0a, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x4e, 0x4f, + 0x4d, 0x45, 0x4d, 0x20, 0x3d, 0x20, 0x31, 0x32, 0x3b, 0x0a, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x42, 0x55, 0x53, 0x59, + 0x20, 0x3d, 0x20, 0x31, 0x36, 0x3b, 0x0a, 0x0a, 0x23, 0x5b, 0x69, 0x66, + 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x64, 0x61, 0x72, 0x77, + 0x69, 0x6e, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x20, 0x45, 0x41, 0x47, 0x41, 0x49, 0x4e, 0x20, 0x3d, 0x20, 0x33, 0x35, + 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, + 0x44, 0x45, 0x41, 0x44, 0x4c, 0x4b, 0x20, 0x3d, 0x20, 0x31, 0x31, 0x3b, + 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x53, 0x43, + 0x5f, 0x4e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x53, + 0x5f, 0x4f, 0x4e, 0x4c, 0x4e, 0x20, 0x3d, 0x20, 0x35, 0x38, 0x3b, 0x0a, + 0x23, 0x5b, 0x65, 0x6c, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, + 0x3d, 0x20, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x5d, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x41, 0x47, 0x41, 0x49, 0x4e, + 0x20, 0x3d, 0x20, 0x31, 0x31, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x45, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x4b, 0x20, + 0x3d, 0x20, 0x33, 0x35, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x20, 0x53, 0x43, 0x5f, 0x4e, 0x50, 0x52, 0x4f, 0x43, 0x45, + 0x53, 0x53, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x4e, 0x4c, 0x4e, 0x20, 0x3d, + 0x20, 0x38, 0x34, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x73, 0x65, 0x5d, + 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x41, + 0x47, 0x41, 0x49, 0x4e, 0x20, 0x3d, 0x20, 0x31, 0x31, 0x3b, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x45, 0x44, 0x45, 0x41, + 0x44, 0x4c, 0x4b, 0x20, 0x3d, 0x20, 0x33, 0x35, 0x3b, 0x0a, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x53, 0x43, 0x5f, 0x4e, 0x50, + 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x4e, + 0x4c, 0x4e, 0x20, 0x3d, 0x20, 0x38, 0x34, 0x3b, 0x0a, 0x23, 0x5b, 0x65, + 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x0a, + }; + file_48.set_content(reinterpret_cast(file_48_data), sizeof(file_48_data)); + auto &file_49 = module.add_file("stdlib:/std/thread/lock.eco"); + static const unsigned char file_49_data[] = { + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x73, 0x6c, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x76, @@ -39294,9 +40531,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x75, 0x65, 0x20, 0x3d, 0x20, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, }; - file_48.set_content(reinterpret_cast(file_48_data), sizeof(file_48_data)); - auto &file_49 = module.add_file("stdlib:/std/thread/task.eco"); - static const unsigned char file_49_data[] = { + file_49.set_content(reinterpret_cast(file_49_data), sizeof(file_49_data)); + auto &file_50 = module.add_file("stdlib:/std/thread/task.eco"); + static const unsigned char file_50_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x4f, 0x6e, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x79, @@ -39491,9 +40728,9 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x61, 0x77, 0x6e, 0x28, 0x24, 0x77, 0x6f, 0x72, 0x6b, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; - file_49.set_content(reinterpret_cast(file_49_data), sizeof(file_49_data)); - auto &file_50 = module.add_file("stdlib:/std/thread/thread.eco"); - static const unsigned char file_50_data[] = { + file_50.set_content(reinterpret_cast(file_50_data), sizeof(file_50_data)); + auto &file_51 = module.add_file("stdlib:/std/thread/thread.eco"); + static const unsigned char file_51_data[] = { 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x4f, 0x6e, 0x65, 0x20, 0x4f, 0x53, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, @@ -39723,60 +40960,1561 @@ void load_stdlib_module(AST::Bundle &bundle, AST::Module &module) 0x64, 0x5f, 0x79, 0x69, 0x65, 0x6c, 0x64, 0x28, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x73, 0x20, 0x60, 0x24, 0x6d, 0x73, 0x60, 0x20, 0x6d, 0x69, - 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2c, 0x20, - 0x63, 0x68, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, - 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, - 0x74, 0x20, 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x35, 0x30, 0x30, 0x20, 0x73, - 0x6f, 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x6c, 0x65, - 0x65, 0x70, 0x0a, 0x20, 0x2a, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x69, - 0x6c, 0x6c, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, - 0x69, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x27, 0x73, 0x20, 0x75, - 0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x2e, - 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x6c, 0x65, - 0x65, 0x70, 0x28, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x6d, 0x73, - 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x0a, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x6c, 0x65, - 0x66, 0x74, 0x20, 0x3d, 0x20, 0x24, 0x6d, 0x73, 0x3b, 0x0a, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x24, 0x6c, - 0x65, 0x66, 0x74, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, - 0x20, 0x24, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x20, 0x3d, 0x20, 0x24, 0x6c, - 0x65, 0x66, 0x74, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x63, 0x68, 0x75, 0x6e, 0x6b, - 0x20, 0x3e, 0x20, 0x35, 0x30, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x63, - 0x68, 0x75, 0x6e, 0x6b, 0x20, 0x3d, 0x20, 0x35, 0x30, 0x30, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x75, 0x73, 0x6c, - 0x65, 0x65, 0x70, 0x28, 0x28, 0x24, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x20, - 0x2a, 0x20, 0x31, 0x30, 0x30, 0x30, 0x29, 0x20, 0x61, 0x73, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x24, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x3d, 0x20, - 0x24, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x2d, 0x20, 0x24, 0x63, 0x68, 0x75, - 0x6e, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, - 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x48, 0x6f, 0x77, 0x20, - 0x6d, 0x61, 0x6e, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x6f, 0x72, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, - 0x2e, 0x20, 0x31, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x74, 0x20, - 0x64, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x2a, - 0x2f, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x75, 0x73, - 0x69, 0x7a, 0x65, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, - 0x74, 0x36, 0x34, 0x20, 0x24, 0x6e, 0x20, 0x3d, 0x20, 0x63, 0x5f, 0x73, - 0x79, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x28, 0x53, 0x43, 0x5f, 0x4e, 0x50, - 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x4e, - 0x4c, 0x4e, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, - 0x20, 0x28, 0x24, 0x6e, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, + 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2e, 0x20, + 0x54, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x63, 0x61, 0x6c, + 0x6c, 0x20, 0x61, 0x73, 0x20, 0x60, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, + 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x60, 0x2c, + 0x20, 0x69, 0x6e, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, + 0x60, 0x24, 0x6d, 0x73, 0x20, 0x61, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x60, 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x20, 0x69, 0x66, 0x20, + 0x60, 0x24, 0x6d, 0x73, 0x60, 0x20, 0x69, 0x73, 0x20, 0x61, 0x62, 0x6f, + 0x76, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x27, 0x73, 0x20, 0x6d, + 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x2e, 0x20, 0x54, 0x68, 0x61, 0x74, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, + 0x6f, 0x66, 0x20, 0x32, 0x39, 0x32, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, + 0x6f, 0x6e, 0x0a, 0x20, 0x2a, 0x20, 0x79, 0x65, 0x61, 0x72, 0x73, 0x3b, + 0x20, 0x73, 0x61, 0x74, 0x75, 0x72, 0x61, 0x74, 0x65, 0x20, 0x72, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62, 0x65, + 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x67, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x2d, + 0x6f, 0x70, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x73, 0x6c, 0x65, 0x65, 0x70, 0x28, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, + 0x24, 0x6d, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, + 0x24, 0x6e, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x6d, 0x61, + 0x74, 0x68, 0x3a, 0x3a, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x54, 0x36, + 0x34, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x24, 0x6d, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, + 0x6d, 0x61, 0x74, 0x68, 0x3a, 0x3a, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, + 0x54, 0x36, 0x34, 0x20, 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x24, 0x6e, 0x20, 0x3d, 0x20, 0x24, 0x6d, 0x73, 0x20, 0x61, 0x73, 0x20, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, + 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x28, 0x73, + 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x64, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x6d, 0x69, 0x6c, 0x6c, + 0x69, 0x73, 0x28, 0x24, 0x6e, 0x29, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x48, 0x6f, 0x77, 0x20, 0x6d, + 0x61, 0x6e, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, + 0x72, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6c, 0x61, 0x74, 0x66, + 0x6f, 0x72, 0x6d, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2e, + 0x20, 0x31, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x74, 0x20, 0x64, + 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, + 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x75, 0x73, 0x69, + 0x7a, 0x65, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x20, 0x24, 0x6e, 0x20, 0x3d, 0x20, 0x63, 0x5f, 0x73, 0x79, + 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x28, 0x53, 0x43, 0x5f, 0x4e, 0x50, 0x52, + 0x4f, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x4e, 0x4c, + 0x4e, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x24, 0x6e, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x24, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x3b, + 0x0a, 0x7d, 0x0a, + }; + file_51.set_content(reinterpret_cast(file_51_data), sizeof(file_51_data)); + auto &file_52 = module.add_file("stdlib:/std/time/duration.eco"); + static const unsigned char file_52_data[] = { + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x73, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x61, 0x20, 0x6d, 0x6f, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x20, 0x2a, + 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x77, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x64, + 0x73, 0x2c, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x20, + 0x43, 0x27, 0x73, 0x20, 0x60, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, + 0x63, 0x60, 0x3a, 0x20, 0x60, 0x24, 0x73, 0x65, 0x63, 0x73, 0x60, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x60, 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x60, + 0x2e, 0x20, 0x4e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, + 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x73, 0x6f, 0x20, + 0x61, 0x0a, 0x20, 0x2a, 0x20, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x60, 0x6e, + 0x61, 0x6e, 0x6f, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x60, 0x20, 0x74, 0x61, + 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x20, 0x61, 0x73, 0x2d, 0x69, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x6c, 0x65, + 0x78, 0x69, 0x63, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, + 0x0a, 0x20, 0x2a, 0x20, 0x61, 0x72, 0x6d, 0x2c, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x60, 0x61, 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x28, + 0x29, 0x60, 0x20, 0x69, 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x66, 0x6c, + 0x6f, 0x6f, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, + 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x65, + 0x76, 0x65, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x65, 0x67, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, + 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x76, 0x61, 0x72, 0x69, 0x61, + 0x6e, 0x74, 0x3a, 0x20, 0x60, 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x60, + 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x60, 0x5b, 0x30, 0x2c, 0x20, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x5d, 0x60, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x60, 0x24, 0x73, 0x65, 0x63, 0x73, 0x60, 0x20, + 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x20, 0x60, 0x2d, 0x31, 0x2e, 0x35, 0x73, + 0x60, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x7b, 0x73, 0x65, + 0x63, 0x73, 0x3a, 0x20, 0x2d, 0x32, 0x2c, 0x20, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x3a, 0x20, 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x7d, 0x60, 0x2c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, + 0x2f, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x2c, + 0x20, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x60, 0x7b, 0x73, 0x65, 0x63, + 0x73, 0x3a, 0x20, 0x2d, 0x31, 0x2c, 0x20, 0x6e, 0x61, 0x6e, 0x6f, 0x73, + 0x3a, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x7d, 0x60, 0x2e, 0x0a, 0x20, 0x2a, 0x20, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x20, 0x61, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x65, 0x74, 0x69, + 0x63, 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x20, + 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x70, 0x6c, + 0x69, 0x63, 0x69, 0x74, 0x20, 0x64, 0x69, 0x76, 0x2f, 0x6d, 0x6f, 0x64, + 0x20, 0x70, 0x6c, 0x75, 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x62, 0x6f, + 0x72, 0x72, 0x6f, 0x77, 0x20, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x2e, + 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x60, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x60, 0x2e, 0x20, + 0x60, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x60, 0x20, 0x77, 0x6f, + 0x75, 0x6c, 0x64, 0x20, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x3b, 0x20, 0x75, 0x6e, 0x6d, 0x61, + 0x72, 0x6b, 0x65, 0x64, 0x0a, 0x20, 0x2a, 0x20, 0x77, 0x6f, 0x75, 0x6c, + 0x64, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x20, 0x61, 0x20, + 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x60, 0x24, 0x6e, 0x61, + 0x6e, 0x6f, 0x73, 0x60, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, + 0x27, 0x73, 0x20, 0x66, 0x6f, 0x6c, 0x64, 0x20, 0x77, 0x6f, 0x75, 0x6c, + 0x64, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x77, + 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x0a, 0x20, 0x2a, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x68, 0x61, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x2d, + 0x77, 0x69, 0x73, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x69, 0x74, 0x20, 0x73, 0x75, + 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x66, 0x72, 0x65, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x3a, 0x20, 0x6e, + 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x75, 0x74, 0x73, 0x69, + 0x64, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x0a, 0x20, 0x2a, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x73, 0x6b, 0x69, 0x70, + 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x73, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x77, 0x72, 0x69, 0x74, 0x69, 0x6e, + 0x67, 0x20, 0x60, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x24, 0x73, 0x65, 0x63, 0x73, 0x2c, 0x20, 0x24, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x29, 0x60, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x77, + 0x69, 0x6c, 0x64, 0x20, 0x60, 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x60, + 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, + 0x6d, 0x65, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, + 0x53, 0x45, 0x43, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, + 0x5f, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, + 0x5f, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, + 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, + 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, + 0x45, 0x43, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x4d, 0x49, 0x43, 0x52, + 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x20, 0x3d, + 0x20, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x53, 0x45, 0x43, 0x53, 0x5f, + 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x20, 0x3d, + 0x20, 0x36, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x20, 0x53, 0x45, 0x43, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x48, + 0x4f, 0x55, 0x52, 0x20, 0x3d, 0x20, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x0a, + 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x20, 0x24, 0x73, 0x65, 0x63, 0x73, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, + 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x60, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x60, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x2d, 0x77, + 0x69, 0x73, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x60, 0x20, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x60, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x60, + 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, + 0x20, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x73, + 0x20, 0x6c, 0x69, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, 0x76, + 0x65, 0x72, 0x79, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x6d, 0x62, + 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x28, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, + 0x24, 0x73, 0x65, 0x63, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x20, 0x3d, 0x20, 0x24, 0x6e, 0x61, 0x6e, + 0x6f, 0x73, 0x20, 0x2f, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, + 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x72, + 0x65, 0x6d, 0x20, 0x3d, 0x20, 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, + 0x25, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, + 0x53, 0x45, 0x43, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x72, 0x65, 0x6d, 0x20, 0x3c, + 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x72, 0x65, 0x6d, 0x20, 0x3d, + 0x20, 0x24, 0x72, 0x65, 0x6d, 0x20, 0x2b, 0x20, 0x4e, 0x41, 0x4e, 0x4f, + 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x20, 0x3d, 0x20, 0x24, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x73, 0x65, + 0x63, 0x73, 0x20, 0x3d, 0x20, 0x24, 0x73, 0x65, 0x63, 0x73, 0x20, 0x2b, + 0x20, 0x24, 0x65, 0x78, 0x74, 0x72, 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, + 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x24, 0x72, 0x65, 0x6d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x63, + 0x73, 0x28, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x6e, 0x29, 0x20, + 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x24, 0x6e, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x28, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x20, 0x24, 0x6e, 0x29, 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x24, + 0x6e, 0x20, 0x2f, 0x20, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x53, 0x5f, 0x50, + 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x2c, 0x20, 0x28, 0x24, 0x6e, 0x20, + 0x25, 0x20, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x53, 0x5f, 0x50, 0x45, 0x52, + 0x5f, 0x53, 0x45, 0x43, 0x29, 0x20, 0x2a, 0x20, 0x4e, 0x41, 0x4e, 0x4f, + 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x28, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x20, 0x24, 0x6e, 0x29, 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x24, 0x6e, 0x20, 0x2f, 0x20, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x5f, + 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x2c, 0x20, 0x28, 0x24, 0x6e, + 0x20, 0x25, 0x20, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x5f, 0x50, 0x45, + 0x52, 0x5f, 0x53, 0x45, 0x43, 0x29, 0x20, 0x2a, 0x20, 0x4e, 0x41, 0x4e, + 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x43, 0x52, 0x4f, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x28, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x20, 0x24, 0x6e, 0x29, 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x30, 0x2c, 0x20, 0x24, 0x6e, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x69, 0x6e, 0x73, + 0x28, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x6e, 0x29, 0x20, 0x3a, + 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x73, 0x65, 0x63, 0x73, 0x28, 0x24, 0x6e, + 0x20, 0x2a, 0x20, 0x53, 0x45, 0x43, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, + 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x6f, 0x75, + 0x72, 0x73, 0x28, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x6e, 0x29, + 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x73, 0x65, 0x63, 0x73, 0x28, + 0x24, 0x6e, 0x20, 0x2a, 0x20, 0x53, 0x45, 0x43, 0x53, 0x5f, 0x50, 0x45, + 0x52, 0x5f, 0x48, 0x4f, 0x55, 0x52, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x2a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x57, 0x68, 0x6f, 0x6c, 0x65, + 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2c, 0x20, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x77, 0x61, 0x72, + 0x64, 0x20, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, + 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x2e, 0x20, 0x60, 0x2d, 0x31, + 0x2e, 0x35, 0x73, 0x60, 0x20, 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x73, + 0x20, 0x60, 0x2d, 0x32, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x61, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x73, 0x28, 0x29, 0x20, 0x3a, 0x20, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x73, 0x65, + 0x63, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x75, 0x62, 0x73, + 0x65, 0x63, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x28, 0x29, 0x20, 0x3a, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x6e, + 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x61, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, + 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x72, 0x65, + 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x65, 0x67, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x73, 0x3a, 0x20, 0x60, 0x73, 0x65, 0x63, 0x73, 0x20, + 0x2a, 0x20, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x53, 0x5f, 0x50, 0x45, 0x52, + 0x5f, 0x53, 0x45, 0x43, 0x20, 0x2b, 0x20, 0x6e, 0x61, 0x6e, 0x6f, 0x73, + 0x20, 0x2f, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, + 0x5f, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x61, 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x28, + 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, + 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x2a, 0x20, 0x4d, 0x49, 0x4c, + 0x4c, 0x49, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x20, + 0x2b, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, + 0x6f, 0x73, 0x20, 0x2f, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, + 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x61, 0x73, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x28, + 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, + 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x2a, 0x20, 0x4d, 0x49, 0x43, + 0x52, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x20, + 0x2b, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, + 0x6f, 0x73, 0x20, 0x2f, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, + 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x2a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x57, 0x68, 0x6f, 0x6c, + 0x65, 0x20, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x2e, 0x20, 0x57, 0x72, 0x61, 0x70, 0x73, 0x20, 0x70, 0x61, 0x73, + 0x74, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x70, 0x6c, 0x75, 0x73, + 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x20, 0x32, 0x39, + 0x32, 0x20, 0x79, 0x65, 0x61, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x28, 0x29, + 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, + 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x2a, 0x20, 0x4e, 0x41, 0x4e, 0x4f, + 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x20, 0x2b, 0x20, + 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x73, 0x5f, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x73, 0x28, 0x29, 0x20, 0x3a, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x36, 0x34, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, + 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x61, 0x73, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x36, 0x34, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x24, 0x74, 0x68, + 0x69, 0x73, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x61, 0x73, + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x36, 0x34, 0x29, 0x20, 0x2f, 0x20, + 0x28, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, + 0x45, 0x43, 0x20, 0x61, 0x73, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x36, + 0x34, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x5f, 0x7a, + 0x65, 0x72, 0x6f, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x6f, 0x6f, 0x6c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, + 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x3d, 0x3d, + 0x20, 0x30, 0x20, 0x26, 0x26, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, + 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x60, + 0x73, 0x65, 0x63, 0x73, 0x20, 0x3c, 0x20, 0x30, 0x60, 0x2e, 0x20, 0x53, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x69, 0x6e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, + 0x3a, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x67, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x24, 0x73, 0x65, 0x63, 0x73, + 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x5f, 0x6e, + 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x3a, 0x20, + 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x73, 0x65, 0x63, + 0x73, 0x20, 0x3c, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x73, + 0x28, 0x29, 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x74, 0x68, 0x69, + 0x73, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x3c, 0x20, 0x30, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x2d, 0x24, 0x74, 0x68, 0x69, + 0x73, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x2c, 0x20, 0x2d, 0x24, 0x74, + 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x2c, + 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, + 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x61, 0x29, 0x20, + 0x2b, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x24, 0x61, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x2b, 0x20, + 0x24, 0x62, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x2c, 0x20, 0x24, 0x61, + 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x2b, 0x20, 0x24, 0x62, + 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x61, 0x29, 0x20, + 0x2d, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x24, 0x61, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x2d, 0x20, + 0x24, 0x62, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x2c, 0x20, 0x24, 0x61, + 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x2d, 0x20, 0x24, 0x62, + 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x2d, 0x28, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x61, 0x29, + 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x2d, 0x24, + 0x61, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x2c, 0x20, 0x2d, 0x24, 0x61, + 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x61, 0x29, 0x20, + 0x3d, 0x3d, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x24, 0x61, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x3d, 0x3d, + 0x20, 0x24, 0x62, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x26, 0x26, + 0x20, 0x24, 0x61, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x3d, + 0x3d, 0x20, 0x24, 0x62, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, + 0x61, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x6f, + 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x24, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x24, 0x62, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x7d, + 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x61, 0x29, + 0x20, 0x3c, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x61, + 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x24, 0x62, + 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x24, 0x61, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x20, 0x3c, 0x20, + 0x24, 0x62, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x20, 0x3c, 0x20, 0x24, 0x62, 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x24, 0x61, 0x29, 0x20, 0x3e, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, + 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x62, 0x20, 0x3c, 0x20, 0x24, 0x61, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x24, 0x61, 0x29, 0x20, 0x3c, 0x3d, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, + 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x24, 0x61, 0x20, 0x3c, 0x20, 0x24, 0x62, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x24, 0x62, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x24, 0x61, 0x29, 0x20, 0x3e, 0x3d, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, + 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x24, 0x62, 0x20, 0x3c, 0x20, 0x24, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x24, 0x62, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, + 0x57, 0x68, 0x6f, 0x6c, 0x65, 0x2d, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, + 0x2e, 0x20, 0x4f, 0x6e, 0x6c, 0x79, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x69, + 0x6e, 0x67, 0x66, 0x75, 0x6c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, + 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x70, 0x6c, 0x75, 0x73, 0x20, + 0x6f, 0x72, 0x20, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x20, 0x32, 0x39, 0x32, + 0x20, 0x79, 0x65, 0x61, 0x72, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x0a, + 0x20, 0x2a, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6e, + 0x6f, 0x73, 0x28, 0x29, 0x60, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x61, 0x29, 0x20, 0x2a, 0x20, + 0x28, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x6e, 0x29, 0x20, 0x3a, + 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x6e, 0x61, 0x6e, + 0x6f, 0x73, 0x28, 0x24, 0x61, 0x2d, 0x3e, 0x61, 0x73, 0x5f, 0x6e, 0x61, + 0x6e, 0x6f, 0x73, 0x28, 0x29, 0x20, 0x2a, 0x20, 0x24, 0x6e, 0x29, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x28, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x6e, 0x29, 0x20, + 0x2a, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x24, 0x61, 0x29, 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x20, 0x2a, 0x20, 0x24, 0x6e, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, + 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68, + 0x6f, 0x6c, 0x65, 0x2d, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x20, 0x64, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, + 0x20, 0x53, 0x61, 0x6d, 0x65, 0x20, 0x32, 0x39, 0x32, 0x2d, 0x79, 0x65, + 0x61, 0x72, 0x20, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x73, 0x20, + 0x60, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x28, 0x29, 0x60, + 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x24, 0x61, 0x29, 0x20, 0x2f, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x20, 0x24, 0x6e, 0x29, 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x24, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, + 0x65, 0x28, 0x27, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x64, 0x69, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x7a, + 0x65, 0x72, 0x6f, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x24, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, - 0x3b, 0x0a, 0x7d, 0x0a, + 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x6e, + 0x61, 0x6e, 0x6f, 0x73, 0x28, 0x24, 0x61, 0x2d, 0x3e, 0x61, 0x73, 0x5f, + 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x28, 0x29, 0x20, 0x2f, 0x20, 0x24, 0x6e, + 0x29, 0x3b, 0x0a, 0x7d, 0x0a, }; - file_50.set_content(reinterpret_cast(file_50_data), sizeof(file_50_data)); + file_52.set_content(reinterpret_cast(file_52_data), sizeof(file_52_data)); + auto &file_53 = module.add_file("stdlib:/std/time/from.eco"); + static const unsigned char file_53_data[] = { + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x73, 0x74, 0x72, 0x3a, + 0x3a, 0x66, 0x72, 0x6f, 0x6d, 0x60, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x60, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x60, 0x2c, 0x20, 0x73, + 0x6f, 0x20, 0x60, 0x22, 0x7b, 0x24, 0x64, 0x7d, 0x22, 0x60, 0x20, 0x69, + 0x73, 0x20, 0x47, 0x6f, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x20, 0x28, + 0x60, 0x31, 0x68, 0x32, 0x6d, 0x33, 0x2e, 0x35, 0x73, 0x60, 0x29, 0x2e, + 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x66, 0x69, 0x6c, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6f, 0x77, 0x6e, + 0x20, 0x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65, 0x20, 0x61, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, + 0x61, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x20, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, 0x63, 0x6f, + 0x20, 0x69, 0x73, 0x20, 0x60, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, + 0x6d, 0x65, 0x60, 0x0a, 0x20, 0x2a, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x62, + 0x65, 0x20, 0x60, 0x73, 0x74, 0x72, 0x60, 0x2e, 0x20, 0x53, 0x70, 0x65, + 0x63, 0x2d, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x3a, + 0x20, 0x60, 0x22, 0x7b, 0x24, 0x64, 0x3a, 0x3e, 0x38, 0x7d, 0x22, 0x60, + 0x20, 0x66, 0x61, 0x69, 0x6c, 0x73, 0x20, 0x62, 0x79, 0x20, 0x6e, 0x61, + 0x6d, 0x65, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x2e, 0x0a, 0x20, + 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x4e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x2d, 0x6d, 0x69, 0x6e, 0x20, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x20, 0x77, 0x72, 0x61, 0x70, + 0x73, 0x2e, 0x20, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, + 0x6c, 0x65, 0x20, 0x61, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, + 0x61, 0x67, 0x6e, 0x69, 0x74, 0x75, 0x64, 0x65, 0x3b, 0x20, 0x6e, 0x6f, + 0x74, 0x20, 0x61, 0x20, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x2e, 0x0a, + 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x20, 0x73, 0x74, 0x72, 0x3b, 0x0a, 0x0a, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x5f, 0x46, + 0x52, 0x41, 0x43, 0x5f, 0x44, 0x49, 0x47, 0x49, 0x54, 0x53, 0x20, 0x3d, + 0x20, 0x36, 0x3b, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x5f, 0x46, 0x52, 0x41, 0x43, 0x5f, 0x44, + 0x49, 0x47, 0x49, 0x54, 0x53, 0x20, 0x3d, 0x20, 0x33, 0x3b, 0x0a, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x5f, + 0x46, 0x52, 0x41, 0x43, 0x5f, 0x44, 0x49, 0x47, 0x49, 0x54, 0x53, 0x20, + 0x3d, 0x20, 0x39, 0x3b, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, + 0x6f, 0x6d, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x73, 0x74, 0x64, + 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x26, 0x20, 0x24, 0x64, 0x29, 0x20, 0x3a, 0x20, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x64, 0x2d, 0x3e, 0x69, 0x73, 0x5f, + 0x7a, 0x65, 0x72, 0x6f, 0x28, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x27, 0x30, 0x73, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x64, + 0x2d, 0x3e, 0x69, 0x73, 0x5f, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x28, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, + 0x6e, 0x63, 0x61, 0x74, 0x28, 0x27, 0x2d, 0x27, 0x2c, 0x20, 0x72, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x24, 0x64, 0x2d, 0x3e, 0x61, 0x62, 0x73, 0x28, 0x29, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x24, 0x64, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x26, 0x20, 0x24, 0x64, 0x29, + 0x20, 0x3a, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x73, + 0x65, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x24, 0x64, 0x2d, 0x3e, 0x61, 0x73, + 0x5f, 0x73, 0x65, 0x63, 0x73, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x20, 0x3d, 0x20, 0x24, 0x64, 0x2d, 0x3e, 0x73, 0x75, 0x62, 0x73, + 0x65, 0x63, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x28, 0x29, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x73, 0x65, + 0x63, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, + 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x3e, 0x3d, 0x20, 0x73, 0x74, 0x64, + 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x4e, 0x41, 0x4e, 0x4f, + 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x28, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x2f, + 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, + 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x49, + 0x4c, 0x4c, 0x49, 0x29, 0x20, 0x61, 0x73, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x24, 0x6e, 0x61, + 0x6e, 0x6f, 0x73, 0x20, 0x25, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, + 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, + 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x29, 0x20, 0x61, 0x73, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x5f, 0x46, 0x52, 0x41, 0x43, 0x5f, + 0x44, 0x49, 0x47, 0x49, 0x54, 0x53, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x27, 0x6d, 0x73, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x20, 0x3e, 0x3d, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, + 0x6d, 0x65, 0x3a, 0x3a, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, + 0x52, 0x5f, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x2f, 0x20, 0x73, 0x74, 0x64, + 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x4e, 0x41, 0x4e, 0x4f, + 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x29, + 0x20, 0x61, 0x73, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x28, 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, + 0x25, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3a, + 0x3a, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x4d, + 0x49, 0x43, 0x52, 0x4f, 0x29, 0x20, 0x61, 0x73, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x49, 0x43, + 0x52, 0x4f, 0x5f, 0x46, 0x52, 0x41, 0x43, 0x5f, 0x44, 0x49, 0x47, 0x49, + 0x54, 0x53, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x75, 0x73, 0x27, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, + 0x28, 0x66, 0x72, 0x6f, 0x6d, 0x28, 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, + 0x20, 0x61, 0x73, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x29, 0x2c, + 0x20, 0x27, 0x6e, 0x73, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, + 0x20, 0x24, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x24, 0x73, + 0x65, 0x63, 0x73, 0x20, 0x2f, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, + 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x53, 0x45, 0x43, 0x53, 0x5f, 0x50, 0x45, + 0x52, 0x5f, 0x48, 0x4f, 0x55, 0x52, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x6d, 0x69, 0x6e, 0x73, 0x20, + 0x3d, 0x20, 0x28, 0x24, 0x73, 0x65, 0x63, 0x73, 0x20, 0x25, 0x20, 0x73, + 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x53, 0x45, + 0x43, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x48, 0x4f, 0x55, 0x52, 0x29, + 0x20, 0x2f, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, + 0x3a, 0x3a, 0x53, 0x45, 0x43, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x4d, + 0x49, 0x4e, 0x55, 0x54, 0x45, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x73, 0x20, 0x3d, 0x20, 0x24, 0x73, + 0x65, 0x63, 0x73, 0x20, 0x25, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, + 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x53, 0x45, 0x43, 0x53, 0x5f, 0x50, 0x45, + 0x52, 0x5f, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x24, 0x6f, + 0x75, 0x74, 0x20, 0x3d, 0x20, 0x27, 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x68, 0x6f, 0x75, 0x72, 0x73, + 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, + 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x66, 0x72, 0x6f, 0x6d, 0x28, + 0x24, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x29, 0x2c, 0x20, 0x27, 0x68, 0x27, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x68, 0x6f, 0x75, 0x72, 0x73, + 0x20, 0x21, 0x3d, 0x20, 0x30, 0x20, 0x7c, 0x7c, 0x20, 0x24, 0x6d, 0x69, + 0x6e, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x6f, 0x75, 0x74, 0x20, + 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x24, 0x6f, 0x75, + 0x74, 0x2c, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x28, 0x24, 0x6d, 0x69, 0x6e, + 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x24, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, + 0x61, 0x74, 0x28, 0x24, 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x27, 0x6d, 0x27, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x24, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, + 0x63, 0x61, 0x74, 0x28, 0x24, 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x66, 0x72, + 0x6f, 0x6d, 0x28, 0x24, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x6e, 0x61, 0x6e, 0x6f, 0x73, + 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, + 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x24, 0x6f, 0x75, 0x74, 0x2c, + 0x20, 0x27, 0x2e, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x63, 0x6f, + 0x6e, 0x63, 0x61, 0x74, 0x28, 0x24, 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x66, + 0x72, 0x61, 0x63, 0x5f, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x28, 0x24, + 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x20, 0x61, 0x73, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x2c, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x5f, 0x46, 0x52, + 0x41, 0x43, 0x5f, 0x44, 0x49, 0x47, 0x49, 0x54, 0x53, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, + 0x74, 0x28, 0x24, 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x27, 0x73, 0x27, 0x29, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x77, 0x68, 0x6f, 0x6c, 0x65, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x66, 0x72, 0x61, + 0x63, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x26, 0x20, 0x24, 0x75, 0x6e, 0x69, 0x74, + 0x29, 0x20, 0x3a, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x66, 0x72, + 0x61, 0x63, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x22, 0x7b, 0x24, 0x77, 0x68, 0x6f, 0x6c, 0x65, 0x7d, 0x7b, + 0x24, 0x75, 0x6e, 0x69, 0x74, 0x7d, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x20, 0x24, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x20, 0x3d, + 0x20, 0x66, 0x72, 0x61, 0x63, 0x5f, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, + 0x28, 0x24, 0x66, 0x72, 0x61, 0x63, 0x2c, 0x20, 0x24, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x22, 0x7b, 0x24, 0x77, 0x68, 0x6f, 0x6c, 0x65, + 0x7d, 0x2e, 0x7b, 0x24, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x7d, 0x7b, + 0x24, 0x75, 0x6e, 0x69, 0x74, 0x7d, 0x22, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x61, 0x63, 0x5f, 0x64, 0x69, + 0x67, 0x69, 0x74, 0x73, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, + 0x24, 0x66, 0x72, 0x61, 0x63, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x20, 0x24, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x20, 0x3a, 0x20, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x24, 0x73, 0x20, 0x3d, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x28, 0x24, 0x66, 0x72, 0x61, 0x63, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, + 0x6e, 0x65, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x24, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x20, 0x61, 0x73, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, + 0x24, 0x73, 0x2d, 0x3e, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x29, 0x20, 0x3c, + 0x20, 0x24, 0x6e, 0x65, 0x65, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x73, 0x20, 0x3d, 0x20, 0x63, + 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x27, 0x30, 0x27, 0x2c, 0x20, 0x24, + 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x24, 0x65, 0x6e, + 0x64, 0x20, 0x3d, 0x20, 0x24, 0x73, 0x2d, 0x3e, 0x73, 0x69, 0x7a, 0x65, + 0x28, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, + 0x6c, 0x65, 0x20, 0x28, 0x24, 0x65, 0x6e, 0x64, 0x20, 0x3e, 0x20, 0x30, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x24, 0x73, 0x2d, 0x3e, 0x62, 0x79, 0x74, 0x65, + 0x28, 0x24, 0x65, 0x6e, 0x64, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x20, 0x21, + 0x3d, 0x20, 0x43, 0x48, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x24, 0x65, + 0x6e, 0x64, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x24, 0x73, 0x2d, 0x3e, 0x73, 0x75, 0x62, 0x28, 0x30, 0x2c, + 0x20, 0x24, 0x65, 0x6e, 0x64, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, + }; + file_53.set_content(reinterpret_cast(file_53_data), sizeof(file_53_data)); + auto &file_54 = module.add_file("stdlib:/std/time/instant.eco"); + static const unsigned char file_54_data[] = { + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x6d, 0x6f, 0x6d, + 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, + 0x6f, 0x6e, 0x6f, 0x74, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x63, 0x6c, 0x6f, + 0x63, 0x6b, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x24, + 0x72, 0x61, 0x77, 0x60, 0x20, 0x69, 0x73, 0x20, 0x74, 0x69, 0x6d, 0x65, + 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, + 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x20, 0x66, 0x69, 0x78, 0x65, + 0x64, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x2e, 0x20, 0x53, 0x75, + 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, + 0x64, 0x64, 0x20, 0x61, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x3b, 0x0a, 0x20, 0x2a, 0x20, 0x64, 0x6f, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x2e, 0x20, + 0x60, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x3a, 0x3a, 0x6e, 0x6f, + 0x77, 0x28, 0x29, 0x60, 0x20, 0x69, 0x73, 0x20, 0x60, 0x63, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x28, 0x43, + 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x4d, 0x4f, 0x4e, 0x4f, 0x54, 0x4f, 0x4e, + 0x49, 0x43, 0x29, 0x60, 0x20, 0x28, 0x51, 0x50, 0x43, 0x20, 0x6f, 0x6e, + 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x29, 0x2e, 0x0a, 0x20, + 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3b, + 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x72, 0x61, 0x77, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x77, 0x28, 0x29, 0x20, 0x3a, 0x20, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x20, 0x24, 0x74, 0x73, 0x20, 0x3d, + 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x28, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, + 0x74, 0x69, 0x6d, 0x65, 0x28, 0x43, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x4d, + 0x4f, 0x4e, 0x4f, 0x54, 0x4f, 0x4e, 0x49, 0x43, 0x2c, 0x20, 0x26, 0x24, + 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x24, 0x74, 0x73, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x2c, 0x20, 0x24, + 0x74, 0x73, 0x2d, 0x3e, 0x6e, 0x73, 0x65, 0x63, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x28, 0x29, 0x20, + 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x3a, 0x3a, 0x6e, 0x6f, 0x77, 0x28, 0x29, 0x20, 0x2d, + 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x69, + 0x6e, 0x63, 0x65, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, + 0x24, 0x65, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x72, 0x29, 0x20, 0x3a, 0x20, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x2d, 0x20, 0x24, 0x65, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x72, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x24, 0x61, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, + 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x20, 0x2d, 0x20, 0x24, 0x62, 0x2d, 0x3e, + 0x72, 0x61, 0x77, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x24, 0x61, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x64, 0x29, 0x20, 0x3a, 0x20, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x28, 0x24, 0x61, 0x2d, 0x3e, 0x72, 0x61, 0x77, + 0x20, 0x2b, 0x20, 0x24, 0x64, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x61, 0x29, 0x20, 0x2d, 0x20, 0x28, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x64, 0x29, + 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x28, 0x24, 0x61, 0x2d, 0x3e, + 0x72, 0x61, 0x77, 0x20, 0x2d, 0x20, 0x24, 0x64, 0x29, 0x3b, 0x0a, 0x7d, + 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x61, 0x29, 0x20, + 0x3d, 0x3d, 0x20, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, + 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x24, 0x61, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x24, + 0x62, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x61, 0x29, 0x20, 0x21, 0x3d, 0x20, + 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x62, 0x29, + 0x20, 0x3a, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x61, 0x20, 0x3d, 0x3d, 0x20, + 0x24, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x24, 0x61, 0x29, 0x20, 0x3c, 0x20, 0x28, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x6f, + 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x20, + 0x3c, 0x20, 0x24, 0x62, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x3b, 0x0a, 0x7d, + 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x61, 0x29, 0x20, + 0x3e, 0x20, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, + 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, + 0x62, 0x20, 0x3c, 0x20, 0x24, 0x61, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x61, 0x29, 0x20, 0x3c, 0x3d, 0x20, + 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x62, 0x29, + 0x20, 0x3a, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x61, 0x20, 0x3c, 0x20, 0x24, + 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x20, 0x3d, + 0x3d, 0x20, 0x24, 0x62, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x24, 0x61, 0x29, 0x20, 0x3e, 0x3d, 0x20, 0x28, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, + 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x24, 0x62, 0x20, 0x3c, 0x20, 0x24, 0x61, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x20, 0x3d, 0x3d, 0x20, + 0x24, 0x62, 0x3b, 0x0a, 0x7d, 0x0a, + }; + file_54.set_content(reinterpret_cast(file_54_data), sizeof(file_54_data)); + auto &file_55 = module.add_file("stdlib:/std/time/libc.eco"); + static const unsigned char file_55_data[] = { + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x50, 0x6c, 0x61, 0x74, 0x66, + 0x6f, 0x72, 0x6d, 0x20, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x62, 0x69, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, + 0x50, 0x4f, 0x53, 0x49, 0x58, 0x20, 0x69, 0x73, 0x20, 0x60, 0x63, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x60, + 0x20, 0x2f, 0x20, 0x60, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x6c, 0x65, 0x65, + 0x70, 0x60, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x60, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x60, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x27, 0x73, 0x20, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a, 0x20, + 0x73, 0x70, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x61, 0x20, + 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x61, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x74, + 0x61, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, + 0x20, 0x74, 0x77, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x77, + 0x69, 0x74, 0x68, 0x20, 0x6e, 0x6f, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x73, 0x20, 0x68, 0x61, 0x73, 0x0a, 0x20, 0x2a, 0x20, 0x6e, 0x65, + 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x3b, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x73, 0x68, 0x69, 0x6d, 0x73, 0x20, 0x62, 0x65, + 0x6c, 0x6f, 0x77, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x20, 0x74, 0x68, 0x6f, + 0x73, 0x65, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x20, 0x73, 0x6f, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x2e, + 0x65, 0x63, 0x6f, 0x2c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x65, 0x63, 0x6f, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, + 0x6c, 0x65, 0x65, 0x70, 0x2e, 0x65, 0x63, 0x6f, 0x0a, 0x20, 0x2a, 0x20, + 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, + 0x61, 0x20, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x63, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2e, 0x0a, + 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x43, 0x4c, 0x4f, 0x43, 0x4b, + 0x5f, 0x4d, 0x4f, 0x4e, 0x4f, 0x54, 0x4f, 0x4e, 0x49, 0x43, 0x60, 0x20, + 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, + 0x60, 0x5f, 0x52, 0x41, 0x57, 0x60, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x6f, + 0x74, 0x68, 0x20, 0x55, 0x6e, 0x69, 0x78, 0x65, 0x73, 0x3a, 0x20, 0x4e, + 0x54, 0x50, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x73, 0x6c, 0x65, 0x77, 0x20, + 0x69, 0x74, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x6e, 0x65, 0x76, 0x65, 0x72, + 0x20, 0x73, 0x74, 0x65, 0x70, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x0a, 0x20, 0x2a, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x47, 0x6f, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x52, 0x75, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6e, + 0x64, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x20, 0x44, 0x61, 0x72, + 0x77, 0x69, 0x6e, 0x27, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x69, 0x73, 0x20, 0x36, 0x3b, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x6f, + 0x6e, 0x65, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x75, 0x73, 0x65, 0x73, + 0x20, 0x31, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, + 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x75, + 0x78, 0x20, 0x75, 0x61, 0x70, 0x69, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x69, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x57, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x51, 0x50, 0x43, 0x20, 0x73, 0x68, + 0x69, 0x6d, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x6f, 0x6e, 0x2e, 0x0a, + 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x43, 0x20, 0x73, 0x79, + 0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x6f, 0x6e, 0x6c, + 0x79, 0x20, 0x62, 0x65, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x65, + 0x64, 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x70, 0x65, 0x72, 0x20, 0x6d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x79, 0x20, 0x6c, 0x69, 0x76, 0x65, 0x20, 0x68, 0x65, 0x72, 0x65, + 0x2e, 0x20, 0x60, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x60, + 0x3a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, 0x6f, + 0x66, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, + 0x69, 0x6d, 0x65, 0x60, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x6d, 0x2c, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6d, 0x65, 0x72, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, + 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, + 0x6d, 0x65, 0x3b, 0x0a, 0x0a, 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, + 0x43, 0x27, 0x73, 0x20, 0x60, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x60, 0x20, 0x6f, 0x6e, + 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x55, 0x6e, 0x69, 0x78, 0x20, + 0x77, 0x65, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x20, 0x66, 0x6f, 0x72, + 0x3a, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x36, 0x34, 0x2d, 0x62, 0x69, 0x74, + 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x2c, 0x20, 0x60, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x74, 0x60, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x60, + 0x6c, 0x6f, 0x6e, 0x67, 0x60, 0x2e, 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, + 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3b, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x73, 0x68, 0x69, 0x6d, 0x73, 0x20, 0x73, 0x74, + 0x69, 0x6c, 0x6c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x6f, 0x6e, + 0x65, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x70, 0x6c, 0x61, 0x69, + 0x6e, 0x20, 0x45, 0x63, 0x68, 0x6f, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, + 0x65, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, + 0x20, 0x2a, 0x20, 0x6c, 0x69, 0x62, 0x63, 0x20, 0x2a, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x73, 0x2a, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x69, 0x74, + 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x63, + 0x6c, 0x61, 0x72, 0x65, 0x64, 0x20, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, + 0x79, 0x2e, 0x20, 0x41, 0x20, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x0a, 0x20, 0x2a, + 0x20, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, + 0x61, 0x69, 0x6c, 0x20, 0x6f, 0x66, 0x66, 0x3b, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x73, 0x65, 0x63, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, + 0x6e, 0x73, 0x65, 0x63, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x5b, 0x69, + 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x74, 0x69, + 0x6d, 0x65, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x63, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x28, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x24, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, + 0x3e, 0x20, 0x24, 0x74, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x6c, 0x65, + 0x65, 0x70, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x6c, 0x65, 0x65, 0x70, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x3e, 0x20, 0x24, 0x72, 0x65, 0x71, + 0x2c, 0x20, 0x70, 0x74, 0x72, 0x3c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, + 0x65, 0x63, 0x3e, 0x20, 0x24, 0x72, 0x65, 0x6d, 0x29, 0x20, 0x3a, 0x20, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, + 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x43, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x4c, + 0x54, 0x49, 0x4d, 0x45, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x0a, 0x23, + 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x64, + 0x61, 0x72, 0x77, 0x69, 0x6e, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x20, 0x43, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x4d, 0x4f, + 0x4e, 0x4f, 0x54, 0x4f, 0x4e, 0x49, 0x43, 0x20, 0x3d, 0x20, 0x36, 0x3b, + 0x0a, 0x23, 0x5b, 0x65, 0x6c, 0x73, 0x65, 0x5d, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x43, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, + 0x4d, 0x4f, 0x4e, 0x4f, 0x54, 0x4f, 0x4e, 0x49, 0x43, 0x20, 0x3d, 0x20, + 0x31, 0x3b, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, 0x0a, 0x23, + 0x5b, 0x69, 0x66, 0x3a, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x77, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x5d, 0x0a, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x72, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x28, 0x70, 0x74, 0x72, 0x3c, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3e, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, + 0x63, 0x79, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, + 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x28, 0x70, 0x74, + 0x72, 0x3c, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3e, 0x20, 0x24, 0x66, 0x72, + 0x65, 0x71, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, + 0x69, 0x6d, 0x65, 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x41, 0x73, + 0x46, 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x20, 0x61, 0x73, 0x20, + 0x63, 0x5f, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, + 0x69, 0x6d, 0x65, 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x41, 0x73, + 0x46, 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x28, 0x70, 0x74, 0x72, + 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3e, 0x20, 0x24, 0x74, 0x69, + 0x6d, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x61, 0x73, 0x20, 0x63, 0x5f, + 0x53, 0x6c, 0x65, 0x65, 0x70, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x20, 0x24, 0x6d, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x45, + 0x50, 0x4f, 0x43, 0x48, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x53, + 0x45, 0x43, 0x53, 0x20, 0x3d, 0x20, 0x31, 0x31, 0x36, 0x34, 0x34, 0x34, + 0x37, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x54, 0x49, 0x4d, 0x45, + 0x5f, 0x54, 0x49, 0x43, 0x4b, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, + 0x45, 0x43, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, + 0x46, 0x49, 0x4c, 0x45, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x4e, 0x41, 0x4e, + 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x54, 0x49, 0x43, 0x4b, 0x20, + 0x3d, 0x20, 0x31, 0x30, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x20, 0x53, 0x4c, 0x45, 0x45, 0x50, 0x5f, 0x43, 0x48, + 0x55, 0x4e, 0x4b, 0x5f, 0x4d, 0x53, 0x20, 0x3d, 0x20, 0x34, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x0a, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x71, 0x70, 0x66, 0x28, + 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x66, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, + 0x79, 0x28, 0x26, 0x24, 0x66, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x66, 0x20, 0x3d, 0x3d, 0x20, 0x30, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x64, 0x69, 0x65, 0x28, 0x27, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, + 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x65, 0x64, 0x20, 0x30, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x24, 0x66, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2f, + 0x20, 0x51, 0x50, 0x46, 0x20, 0x69, 0x73, 0x20, 0x62, 0x6f, 0x6f, 0x74, + 0x2d, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x2e, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x73, + 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x2c, 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2d, + 0x73, 0x61, 0x66, 0x65, 0x3a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x2c, + 0x20, 0x6e, 0x6f, 0x74, 0x0a, 0x2f, 0x2f, 0x20, 0x61, 0x20, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x20, 0x41, 0x20, 0x64, 0x75, 0x6d, + 0x6d, 0x79, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x73, 0x6f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x68, 0x61, 0x73, + 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x6f, 0x72, 0x3b, 0x20, 0x6e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x73, 0x20, 0x6f, + 0x6e, 0x65, 0x2e, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x71, 0x70, 0x63, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x24, + 0x70, 0x61, 0x64, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, + 0x66, 0x72, 0x65, 0x71, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x71, 0x70, 0x66, 0x28, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x67, 0x65, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x20, 0x24, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x70, 0x74, + 0x72, 0x3c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x3e, 0x20, + 0x24, 0x74, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, + 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x3d, 0x3d, 0x20, 0x43, 0x4c, 0x4f, + 0x43, 0x4b, 0x5f, 0x4d, 0x4f, 0x4e, 0x4f, 0x54, 0x4f, 0x4e, 0x49, 0x43, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x5f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x72, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x28, 0x26, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x66, 0x20, 0x3d, 0x20, 0x71, 0x70, + 0x63, 0x3a, 0x3a, 0x24, 0x66, 0x72, 0x65, 0x71, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x74, 0x73, 0x2d, 0x3e, 0x73, + 0x65, 0x63, 0x20, 0x3d, 0x20, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, + 0x2f, 0x20, 0x24, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x24, 0x74, 0x73, 0x2d, 0x3e, 0x6e, 0x73, 0x65, 0x63, 0x20, + 0x3d, 0x20, 0x28, 0x28, 0x24, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x25, + 0x20, 0x24, 0x66, 0x29, 0x20, 0x2a, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, + 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, 0x29, 0x20, 0x2f, 0x20, + 0x24, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x20, 0x24, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x47, 0x65, + 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x50, + 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x41, 0x73, 0x46, 0x69, 0x6c, 0x65, + 0x54, 0x69, 0x6d, 0x65, 0x28, 0x26, 0x24, 0x74, 0x69, 0x63, 0x6b, 0x73, + 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x20, 0x24, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x20, + 0x3d, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x54, + 0x49, 0x43, 0x4b, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, + 0x20, 0x61, 0x73, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x73, + 0x65, 0x63, 0x73, 0x5f, 0x31, 0x36, 0x30, 0x31, 0x20, 0x3d, 0x20, 0x28, + 0x24, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x20, 0x2f, 0x20, 0x24, 0x70, 0x65, + 0x72, 0x5f, 0x73, 0x65, 0x63, 0x29, 0x20, 0x61, 0x73, 0x20, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x20, 0x24, 0x73, 0x75, 0x62, 0x73, 0x65, 0x63, 0x20, 0x3d, + 0x20, 0x28, 0x24, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x20, 0x25, 0x20, 0x24, + 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x29, 0x20, 0x61, 0x73, 0x20, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x24, + 0x74, 0x73, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x20, 0x3d, 0x20, 0x24, 0x73, + 0x65, 0x63, 0x73, 0x5f, 0x31, 0x36, 0x30, 0x31, 0x20, 0x2d, 0x20, 0x46, + 0x49, 0x4c, 0x45, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x45, 0x50, 0x4f, 0x43, + 0x48, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x53, 0x45, 0x43, 0x53, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x24, 0x74, 0x73, 0x2d, 0x3e, 0x6e, + 0x73, 0x65, 0x63, 0x20, 0x3d, 0x20, 0x24, 0x73, 0x75, 0x62, 0x73, 0x65, + 0x63, 0x20, 0x2a, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x54, 0x49, 0x4d, 0x45, + 0x5f, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x54, + 0x49, 0x43, 0x4b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x6c, + 0x65, 0x65, 0x70, 0x28, 0x70, 0x74, 0x72, 0x3c, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x70, 0x65, 0x63, 0x3e, 0x20, 0x24, 0x72, 0x65, 0x71, 0x2c, 0x20, + 0x70, 0x74, 0x72, 0x3c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, + 0x3e, 0x20, 0x24, 0x72, 0x65, 0x6d, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x20, 0x24, 0x6d, 0x73, 0x20, 0x3d, 0x20, 0x24, 0x72, + 0x65, 0x71, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x20, 0x2a, 0x20, 0x4d, 0x49, + 0x4c, 0x4c, 0x49, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x43, + 0x20, 0x2b, 0x20, 0x28, 0x24, 0x72, 0x65, 0x71, 0x2d, 0x3e, 0x6e, 0x73, + 0x65, 0x63, 0x20, 0x2b, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, + 0x45, 0x52, 0x5f, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x20, 0x2d, 0x20, 0x31, + 0x29, 0x20, 0x2f, 0x20, 0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x5f, 0x50, 0x45, + 0x52, 0x5f, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x6d, 0x73, 0x20, 0x3c, 0x20, + 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x24, 0x6d, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, + 0x6c, 0x65, 0x20, 0x28, 0x24, 0x6d, 0x73, 0x20, 0x3e, 0x20, 0x30, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x20, + 0x3d, 0x20, 0x24, 0x6d, 0x73, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x20, 0x3e, 0x20, 0x53, 0x4c, 0x45, 0x45, 0x50, 0x5f, 0x43, + 0x48, 0x55, 0x4e, 0x4b, 0x5f, 0x4d, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, + 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x20, 0x3d, 0x20, 0x53, 0x4c, 0x45, 0x45, + 0x50, 0x5f, 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x5f, 0x4d, 0x53, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x5f, 0x53, 0x6c, 0x65, + 0x65, 0x70, 0x28, 0x24, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x20, 0x61, 0x73, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x6d, 0x73, 0x20, 0x3d, 0x20, + 0x24, 0x6d, 0x73, 0x20, 0x2d, 0x20, 0x24, 0x63, 0x68, 0x75, 0x6e, 0x6b, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x24, 0x72, 0x65, 0x6d, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x24, 0x72, 0x65, 0x6d, + 0x2d, 0x3e, 0x6e, 0x73, 0x65, 0x63, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, + 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x5b, 0x65, 0x6e, 0x64, 0x5d, 0x0a, + }; + file_55.set_content(reinterpret_cast(file_55_data), sizeof(file_55_data)); + auto &file_56 = module.add_file("stdlib:/std/time/sleep.eco"); + static const unsigned char file_56_data[] = { + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x53, 0x6c, 0x65, 0x65, 0x70, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, + 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x69, 0x6d, 0x6d, 0x65, 0x64, + 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x2e, 0x20, 0x4e, 0x65, 0x67, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x69, + 0x73, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x2d, 0x6f, 0x70, 0x2c, 0x20, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x52, 0x75, 0x73, 0x74, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x6f, 0x2e, 0x0a, 0x20, 0x2a, 0x20, + 0x54, 0x68, 0x65, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, + 0x61, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x2c, 0x20, + 0x73, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, + 0x69, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x6f, 0x76, + 0x65, 0x72, 0x20, 0x60, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x6c, 0x65, 0x65, + 0x70, 0x60, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x72, 0x65, 0x74, 0x72, + 0x69, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x0a, 0x20, 0x2a, 0x20, 0x45, 0x49, + 0x4e, 0x54, 0x52, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x20, + 0x60, 0x24, 0x72, 0x65, 0x6d, 0x60, 0x20, 0x69, 0x73, 0x20, 0x7a, 0x65, + 0x72, 0x6f, 0x65, 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, + 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x3a, 0x20, + 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x45, 0x49, 0x4e, 0x54, 0x52, 0x20, + 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x76, + 0x65, 0x73, 0x20, 0x69, 0x74, 0x0a, 0x20, 0x2a, 0x20, 0x7a, 0x65, 0x72, + 0x6f, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x69, 0x74, 0x2c, 0x20, + 0x61, 0x20, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x20, 0x6f, 0x66, 0x20, 0x70, + 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x6e, 0x6f, 0x20, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x20, 0x62, 0x69, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, + 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, 0x69, 0x74, + 0x20, 0x75, 0x70, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, + 0x65, 0x78, 0x74, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x68, 0x75, 0x6e, + 0x6b, 0x73, 0x20, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x75, 0x72, 0x20, 0x62, + 0x69, 0x6c, 0x6c, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x73, 0x2c, 0x20, 0x74, + 0x68, 0x65, 0x0a, 0x20, 0x2a, 0x20, 0x60, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x60, 0x20, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, + 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x35, 0x30, 0x30, 0x20, 0x6d, + 0x73, 0x20, 0x63, 0x68, 0x6f, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6f, + 0x6e, 0x20, 0x55, 0x6e, 0x69, 0x78, 0x3b, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x77, 0x61, 0x73, 0x20, 0x60, 0x75, 0x73, 0x6c, 0x65, 0x65, 0x70, + 0x60, 0x27, 0x73, 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x20, + 0x2a, 0x2f, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x20, 0x73, 0x74, 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3b, + 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x28, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x64, 0x29, + 0x20, 0x3a, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x64, 0x20, 0x3c, 0x3d, 0x20, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x7a, 0x65, + 0x72, 0x6f, 0x28, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x20, 0x24, 0x72, 0x65, 0x71, + 0x20, 0x3d, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x28, + 0x24, 0x64, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x73, 0x2c, 0x20, 0x24, 0x64, + 0x2d, 0x3e, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x74, 0x72, + 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x20, 0x24, + 0x72, 0x65, 0x6d, 0x20, 0x3d, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, + 0x65, 0x63, 0x28, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, + 0x24, 0x72, 0x63, 0x20, 0x3d, 0x20, 0x63, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x6c, 0x65, 0x65, 0x70, 0x28, 0x26, 0x24, 0x72, 0x65, 0x71, 0x2c, + 0x20, 0x26, 0x24, 0x72, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x72, + 0x63, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x72, 0x65, 0x6d, 0x2d, 0x3e, 0x73, + 0x65, 0x63, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x26, 0x26, 0x20, 0x24, + 0x72, 0x65, 0x6d, 0x2d, 0x3e, 0x6e, 0x73, 0x65, 0x63, 0x20, 0x3d, 0x3d, + 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x72, 0x65, + 0x71, 0x20, 0x3d, 0x20, 0x24, 0x72, 0x65, 0x6d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, + }; + file_56.set_content(reinterpret_cast(file_56_data), sizeof(file_56_data)); + auto &file_57 = module.add_file("stdlib:/std/time/timestamp.eco"); + static const unsigned char file_57_data[] = { + 0x2f, 0x2a, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x41, 0x20, 0x6d, 0x6f, 0x6d, + 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, + 0x61, 0x6c, 0x6c, 0x20, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x20, 0x61, + 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x31, + 0x39, 0x37, 0x30, 0x2d, 0x30, 0x31, 0x2d, 0x30, 0x31, 0x54, 0x30, 0x30, + 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x5a, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, + 0x20, 0x2a, 0x20, 0x4e, 0x6f, 0x20, 0x63, 0x61, 0x6c, 0x65, 0x6e, 0x64, + 0x61, 0x72, 0x2c, 0x20, 0x6e, 0x6f, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x7a, + 0x6f, 0x6e, 0x65, 0x2c, 0x20, 0x6e, 0x6f, 0x20, 0x60, 0x73, 0x74, 0x72, + 0x3a, 0x3a, 0x66, 0x72, 0x6f, 0x6d, 0x60, 0x2e, 0x20, 0x48, 0x75, 0x6d, + 0x61, 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x3b, 0x20, 0x61, 0x20, 0x62, 0x61, 0x72, + 0x65, 0x2d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x77, 0x6f, 0x75, 0x6c, + 0x64, 0x20, 0x62, 0x65, 0x20, 0x6b, 0x65, 0x70, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x65, 0x76, 0x65, 0x72, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x2e, + 0x20, 0x60, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, + 0x3a, 0x6e, 0x6f, 0x77, 0x28, 0x29, 0x60, 0x20, 0x69, 0x73, 0x0a, 0x20, + 0x2a, 0x20, 0x60, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, + 0x74, 0x69, 0x6d, 0x65, 0x28, 0x43, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, + 0x45, 0x41, 0x4c, 0x54, 0x49, 0x4d, 0x45, 0x29, 0x60, 0x20, 0x28, 0x47, + 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, + 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x41, 0x73, 0x46, 0x69, 0x6c, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x57, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x73, 0x29, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x73, 0x74, + 0x64, 0x3a, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x3b, 0x0a, 0x0a, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x72, 0x61, 0x77, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x6e, 0x6f, 0x77, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x20, 0x24, 0x74, 0x73, 0x20, 0x3d, + 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x63, 0x28, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, + 0x74, 0x69, 0x6d, 0x65, 0x28, 0x43, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, + 0x45, 0x41, 0x4c, 0x54, 0x49, 0x4d, 0x45, 0x2c, 0x20, 0x26, 0x24, 0x74, + 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x24, 0x74, 0x73, 0x2d, 0x3e, 0x73, 0x65, 0x63, 0x2c, 0x20, + 0x24, 0x74, 0x73, 0x2d, 0x3e, 0x6e, 0x73, 0x65, 0x63, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x73, + 0x65, 0x63, 0x73, 0x28, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, 0x73, + 0x65, 0x63, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x28, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x73, 0x65, + 0x63, 0x73, 0x28, 0x24, 0x73, 0x65, 0x63, 0x73, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6d, 0x69, + 0x6c, 0x6c, 0x69, 0x73, 0x28, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x20, 0x24, + 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x29, 0x20, 0x3a, 0x20, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x28, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, + 0x3a, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x28, 0x24, 0x6d, 0x69, 0x6c, + 0x6c, 0x69, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x28, 0x29, + 0x20, 0x3a, 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, + 0x69, 0x73, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x2a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x57, 0x68, 0x6f, 0x6c, 0x65, + 0x20, 0x55, 0x6e, 0x69, 0x78, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x2c, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, + 0x74, 0x6f, 0x77, 0x61, 0x72, 0x64, 0x20, 0x6e, 0x65, 0x67, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, + 0x2e, 0x20, 0x50, 0x72, 0x65, 0x2d, 0x31, 0x39, 0x37, 0x30, 0x20, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x20, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x23, 0x5b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x73, 0x65, + 0x63, 0x73, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, + 0x68, 0x69, 0x73, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x2d, 0x3e, 0x61, 0x73, + 0x5f, 0x73, 0x65, 0x63, 0x73, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, + 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, 0x69, + 0x73, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x2d, 0x3e, 0x61, 0x73, 0x5f, 0x6d, + 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x2a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x57, 0x68, 0x6f, 0x6c, 0x65, + 0x20, 0x55, 0x6e, 0x69, 0x78, 0x20, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2e, 0x20, 0x57, 0x72, 0x61, 0x70, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x32, 0x32, 0x36, 0x32, 0x2e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x5b, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x74, 0x68, + 0x69, 0x73, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x2d, 0x3e, 0x61, 0x73, 0x5f, + 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x20, 0x24, 0x61, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, + 0x20, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, + 0x61, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x20, 0x2d, 0x20, 0x24, 0x62, 0x2d, + 0x3e, 0x72, 0x61, 0x77, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, 0x61, 0x29, 0x20, 0x2b, 0x20, 0x28, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x64, 0x29, + 0x20, 0x3a, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x28, + 0x24, 0x61, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x20, 0x2b, 0x20, 0x24, 0x64, + 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x20, 0x24, 0x61, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x64, 0x29, 0x20, 0x3a, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x28, 0x24, 0x61, 0x2d, + 0x3e, 0x72, 0x61, 0x77, 0x20, 0x2d, 0x20, 0x24, 0x64, 0x29, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, + 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, + 0x61, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, + 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x2d, 0x3e, 0x72, 0x61, 0x77, + 0x20, 0x3d, 0x3d, 0x20, 0x24, 0x62, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, + 0x24, 0x61, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, + 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x24, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x24, 0x62, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, + 0x24, 0x61, 0x29, 0x20, 0x3c, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, + 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x2d, 0x3e, 0x72, 0x61, 0x77, + 0x20, 0x3c, 0x20, 0x24, 0x62, 0x2d, 0x3e, 0x72, 0x61, 0x77, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, + 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, + 0x61, 0x29, 0x20, 0x3e, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x6f, + 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x24, 0x62, 0x20, 0x3c, 0x20, 0x24, 0x61, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, + 0x24, 0x61, 0x29, 0x20, 0x3c, 0x3d, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, 0x62, 0x29, 0x20, 0x3a, 0x20, + 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x24, 0x61, 0x20, 0x3c, 0x20, 0x24, 0x62, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x24, + 0x62, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x20, 0x24, 0x61, 0x29, 0x20, 0x3e, 0x3d, 0x20, 0x28, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x24, 0x62, 0x29, 0x20, + 0x3a, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x24, 0x62, 0x20, 0x3c, 0x20, 0x24, 0x61, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x24, 0x61, 0x20, 0x3d, 0x3d, + 0x20, 0x24, 0x62, 0x3b, 0x0a, 0x7d, 0x0a, + }; + file_57.set_content(reinterpret_cast(file_57_data), sizeof(file_57_data)); } } diff --git a/stdlib/module.eco b/stdlib/module.eco index 27be2bee..7d4b07a3 100644 --- a/stdlib/module.eco +++ b/stdlib/module.eco @@ -26,8 +26,21 @@ #[sources: "std/env/*.eco"] #[sources: "std/io/*.eco"] #[sources: "std/thread/*.eco"] +#[sources: "std/time/*.eco"] // glibc before 2.34 keeps pthread out of libc. Darwin's libpthread is libSystem. // `runtime: process` is the JIT half: there is no loadable `libpthread.so` on // modern Linux, and the symbols are already mapped. Native `build` still gets `-lpthread`. +// Windows has no libpthread; std::thread talks to Win32 and the static-once helper +// names GetCurrentThreadId. +// +// glibc also keeps libm out of libc. LLVM lowers some math intrinsics (tanh, +// atan2) to libcalls, so a native `build` needs `-lm`. Darwin's libm is +// libSystem; `-lm` still works. `runtime: process` is the JIT half again: +// echoc is itself linked against libm, and `--target-os linux` on Windows +// must not try to open `libm.dll`. Windows UCRT already has the symbols; +// lld-link looking for m.lib is a missing-library error. +#[if: os != windows] #[link: lib { name: "pthread", runtime: process }] +#[link: lib { name: "m", runtime: process }] +#[end] diff --git a/stdlib/std/env/dirs.eco b/stdlib/std/env/dirs.eco index 46718f47..dfa77a6d 100644 --- a/stdlib/std/env/dirs.eco +++ b/stdlib/std/env/dirs.eco @@ -58,16 +58,25 @@ public function cwd() : string */ public function home() : string::view? { +#[if: os == windows] + return var('USERPROFILE'); +#[else] return var('HOME'); +#[end] } /** * The directory for temporary files, `/tmp` when `TMPDIR` is not set. * * Not nullable, because unlike `HOME` there *is* an honest answer when the variable is missing: the - * path every POSIX system has. + * path every POSIX system has. Windows answers `TEMP`, then `TMP`, then the current directory - + * there is no `/tmp`. */ public function tmp() : string::view { +#[if: os == windows] + return var('TEMP') ?? var('TMP', '.'); +#[else] return var('TMPDIR', '/tmp'); +#[end] } diff --git a/stdlib/std/env/libc.eco b/stdlib/std/env/libc.eco index dc8f9c17..7eb02ef4 100644 --- a/stdlib/std/env/libc.eco +++ b/stdlib/std/env/libc.eco @@ -25,7 +25,10 @@ internal extern { // returns null when the name is not set, and a pointer into the environment block when it is. // the bytes belong to the process, not to the caller function getenv as c_getenv(ptr $name) : ptr; +} +#[if: os != windows] +internal extern { // writes the path into `$buf` and hands it back, or null when it does not fit - which is the // only failure `cwd` has to distinguish, and why it grows its buffer rather than // reading errno @@ -33,6 +36,30 @@ internal extern { function getpid as c_getpid() : int32; } +#[end] + +#[if: os == windows] +internal extern { + function _getcwd as c_getcwd(ptr $buf, usize $size) : ptr; + function _getpid as c_getpid() : int32; + + function GetModuleFileNameW as c_GetModuleFileNameW( + ptr $module, ptr $buf, uint32 $size) : uint32; + + function WideCharToMultiByte as c_WideCharToMultiByte( + uint32 $codepage, + uint32 $flags, + ptr $wide, + int32 $wide_n, + ptr $utf8, + int32 $utf8_n, + ptr $default_char, + ptr $used_default + ) : int32; +} + +internal const uint32 CP_UTF8 = 65001; +#[end] // **the one platform-specific binding in the module**, and the reason conditional compilation exists. // diff --git a/stdlib/std/env/process.eco b/stdlib/std/env/process.eco index 04f071b8..7d4b4497 100644 --- a/stdlib/std/env/process.eco +++ b/stdlib/std/env/process.eco @@ -237,6 +237,49 @@ public function exe() : string } die('the executable path does not fit in 64 KiB'); +#[elif: os == windows] + // GetModuleFileNameW writes UTF-16. grow until it fits, then WideCharToMultiByte to + // UTF-8, which is what every Echo `string` is + uint32 $wide_cap = 256; + + while ($wide_cap <= 32768) { + ptr $wide = mem::alloc($wide_cap); + uint32 $written = c_GetModuleFileNameW(ptr(null), $wide, $wide_cap); + + if ($written == 0) { + mem::free($wide); + die('GetModuleFileNameW failed'); + } + + if ($written >= $wide_cap) { + mem::free($wide); + $wide_cap = $wide_cap * 2; + continue; + } + + int32 $utf8_n = c_WideCharToMultiByte( + CP_UTF8, 0, $wide, $written as int32, + ptr(null), 0, ptr(null), ptr(null)); + + if ($utf8_n <= 0) { + mem::free($wide); + die('WideCharToMultiByte failed'); + } + + ptr $utf8 = mem::alloc(($utf8_n + 1) as usize); + $utf8:$[$utf8_n] = 0; + c_WideCharToMultiByte( + CP_UTF8, 0, $wide, $written as int32, + $utf8, $utf8_n, ptr(null), ptr(null)); + mem::free($wide); + + string $path = str::from($utf8); + mem::free($utf8); + + return $path; + } + + die('the executable path does not fit in 32 KiB of UTF-16'); #[else] return str::from(arg(0)->cstr()); #[end] diff --git a/stdlib/std/io/console.eco b/stdlib/std/io/console.eco new file mode 100644 index 00000000..b46ca82d --- /dev/null +++ b/stdlib/std/io/console.eco @@ -0,0 +1,157 @@ +/** + * Writing UTF-8 to a Windows console. + * + * Unix `write(2)` to a terminal is the bytes, and the terminal draws one cell + * per UTF-8 glyph. Windows `_write` to a console is decoded with the OEM code + * page (850, 437) even inside Windows Terminal, so a glyph two or three bytes + * wide lands as that many CP850 characters and a picture falls apart. The + * documented way to write Unicode to a console is `WriteConsoleW`. + * + * This is the Windows half of `c_write`. A pipe or a file fails + * `GetConsoleMode` and `c_write` falls through to `_write` of the same UTF-8 + * - which is what a redirect and every `tests_eco/io` golden see. No public + * API: `stdout->write` is still the surface. + */ + +namespace std::io; + +#[if: os == windows] +internal const uint32 CONSOLE_CP_UTF8 = 65001; +internal const uint32 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; + +/** + * `$count` bytes of UTF-8 at `$buf`, drawn on the console `$fd` names, or -1 + * when `$fd` is not a console. + * + * The count it returns is the UTF-8 length, not the WCHAR count + * `WriteConsoleW` reports, so `raw_write_all` does not retry at a UTF-16 + * offset. + */ +internal function console_write(int32 $fd, ptr $buf, usize $count) : isize +{ + ptr $handle = c_get_osfhandle($fd); + uint32 $mode = 0; + + if (c_GetConsoleMode($handle, &$mode) == 0) { + return -1; + } + + // so `\x1b[H` and `\x1b[2J` move the cursor instead of printing as `←[H`. + // a mode bit, not an alternate screen - sticky after Ctrl-C, and benign + c_SetConsoleMode($handle, $mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + if ($count == 0) { + return 0; + } + + // WriteConsoleW does not translate `\n` the way UCRT `_write` in text + // mode does. a lone LF walks the cursor down without returning to column + // zero. skip an LF that is already preceded by CR, so a caller who sent + // CRLF is not doubled + usize $extra = 0; + + foreach (0 .. $count as $i) { + if ($buf:$[$i] == 10) { + if ($i == 0) { + $extra = $extra + 1; + } + else { + if ($buf:$[$i - 1] != 13) { + $extra = $extra + 1; + } + } + } + } + + ptr $utf8 = $buf; + int32 $utf8_n = $count as int32; + + ptr $expanded = ptr(null); + + if ($extra != 0) { + $expanded:$ = mem::alloc($count + $extra); + + if ($expanded:$ == null) { + return -1; + } + + usize $at = 0; + + foreach (0 .. $count as $i) { + uint8 $b = $buf:$[$i]; + + if ($b == 10) { + if ($i == 0) { + $expanded:$[$at] = 13; + $at = $at + 1; + } + else { + if ($buf:$[$i - 1] != 13) { + $expanded:$[$at] = 13; + $at = $at + 1; + } + } + } + + $expanded:$[$at] = $b; + $at = $at + 1; + } + + $utf8:$ = $expanded:$; + $utf8_n = $at as int32; + } + + int32 $wide_n = c_MultiByteToWideChar( + CONSOLE_CP_UTF8, 0, $utf8, $utf8_n, ptr(null), 0); + + if ($wide_n <= 0) { + if ($expanded:$ != null) { + mem::free($expanded); + } + + return -1; + } + + ptr $wide = mem::alloc($wide_n as usize); + + if ($wide:$ == null) { + if ($expanded:$ != null) { + mem::free($expanded); + } + + return -1; + } + + c_MultiByteToWideChar(CONSOLE_CP_UTF8, 0, $utf8, $utf8_n, $wide, $wide_n); + + if ($expanded:$ != null) { + mem::free($expanded); + } + + usize $off = 0; + usize $need = $wide_n as usize; + + while ($off < $need) { + uint32 $written = 0; + uint32 $chunk = ($need - $off) as uint32; + int32 $ok = c_WriteConsoleW( + $handle, $wide:$ + $off, $chunk, &$written, ptr(null)); + + if ($ok == 0) { + mem::free($wide); + return -1; + } + + if ($written == 0) { + mem::free($wide); + return -1; + } + + $off = $off + $written; + } + + mem::free($wide); + + return $count as isize; +} +#[end] diff --git a/stdlib/std/io/error.eco b/stdlib/std/io/error.eco index de56ebf8..cb7ef11c 100644 --- a/stdlib/std/io/error.eco +++ b/stdlib/std/io/error.eco @@ -83,12 +83,14 @@ public enum whence internal function flags_of(filemode $mode) : int32 { - return match ($mode) { + int32 $flags = match ($mode) { filemode::read => O_RDONLY | O_CLOEXEC, filemode::write => O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, filemode::append => O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, filemode::readwrite => O_RDWR | O_CLOEXEC, }; + + return $flags | O_BINARY; } internal function whence_of(whence $from) : int32 diff --git a/stdlib/std/io/libc.eco b/stdlib/std/io/libc.eco index 7a91d1de..052bc1d8 100644 --- a/stdlib/std/io/libc.eco +++ b/stdlib/std/io/libc.eco @@ -14,6 +14,7 @@ namespace std::io; +#[if: os != windows] internal extern { // the raw, unbuffered writes and reads. deliberately **not** `fwrite`/`fread`: a `string` need // not be NUL-terminated, its length is always known, and a second layer of buffering under one @@ -36,6 +37,70 @@ internal extern { function access as c_access(ptr $path, int32 $amode) : int32; function strerror as c_strerror(int32 $code) : ptr; } +#[end] + +#[if: os == windows] +// UCRT: `_write`/`_read` return `int` and take `unsigned int` counts. wrapping +// them keeps the rest of the module on `isize`/`usize` the way POSIX does. +// `O_BINARY` is or'd in `flags_of` - text mode would eat `\n` +internal extern { + function _write as c_write_raw(int32 $fd, ptr $buf, uint32 $count) : int32; + function _read as c_read_raw(int32 $fd, ptr $buf, uint32 $count) : int32; + function fflush as c_fflush(ptr $stream) : int32; + function _open as c_open(ptr $path, int32 $flags, variadic_args $args) : int32; + function _close as c_close(int32 $fd) : int32; + function _lseeki64 as c_lseek(int32 $fd, int64 $offset, int32 $whence) : int64; + function _unlink as c_unlink(ptr $path) : int32; + function rename as c_rename(ptr $from, ptr $to) : int32; + function _access as c_access(ptr $path, int32 $amode) : int32; + function strerror as c_strerror(int32 $code) : ptr; + function _errno as c_errno_location() : ptr; + + // fd → HANDLE. the console APIs below take a handle, and `_write` of UTF-8 + // to that handle is decoded as OEM (850, 437), which is why `c_write` goes + // through `console_write` first. pinned in the JIT next to `_write` for + // the same msvcrt-vs-UCRT reason + function _get_osfhandle as c_get_osfhandle(int32 $fd) : ptr; +} + +internal extern { + function GetConsoleMode as c_GetConsoleMode(ptr $h, ptr $mode) : int32; + function SetConsoleMode as c_SetConsoleMode(ptr $h, uint32 $mode) : int32; + function WriteConsoleW as c_WriteConsoleW( + ptr $h, + ptr $text, + uint32 $n, + ptr $written, + ptr $reserved + ) : int32; + function MultiByteToWideChar as c_MultiByteToWideChar( + uint32 $cp, + uint32 $flags, + ptr $utf8, + int32 $utf8_n, + ptr $wide, + int32 $wide_n + ) : int32; +} + +// a console is not `_write`. `console_write` answers the byte count it +// displayed, or -1 when `$fd` is a pipe or a file and `_write` should run +internal function c_write(int32 $fd, ptr $buf, usize $count) : isize +{ + isize $n = console_write($fd, $buf, $count); + + if ($n >= 0) { + return $n; + } + + return c_write_raw($fd, $buf, $count as uint32) as isize; +} + +internal function c_read(int32 $fd, ptr $buf, usize $count) : isize +{ + return c_read_raw($fd, $buf, $count as uint32) as isize; +} +#[end] // errno is a thread-local behind a function, and the function's name is the platform. read the // int it points at immediately - nothing else in the caller may touch libc first @@ -47,17 +112,11 @@ internal extern { internal extern { function __errno_location as c_errno_location() : ptr; } -#[else] -// so `--target-os windows` still typechecks the stdlib. the values are Linux's; nothing here -// claims to run -internal extern { - function __errno_location as c_errno_location() : ptr; -} #[end] -// `O_*` values are not the same number on Darwin and Linux. the rest of this module names these -// constants and never the literals, so a flag used on the wrong host is a missing-symbol problem -// at compile time rather than a silent wrong open +// `O_*` values are not the same number on Darwin, Linux and Windows. the rest of this module names +// these constants and never the literals, so a flag used on the wrong host is a missing-symbol +// problem at compile time rather than a silent wrong open #[if: os == darwin] internal const int32 O_RDONLY = 0; internal const int32 O_WRONLY = 1; @@ -66,6 +125,7 @@ internal const int32 O_APPEND = 8; internal const int32 O_CREAT = 512; internal const int32 O_TRUNC = 1024; internal const int32 O_CLOEXEC = 16777216; +internal const int32 O_BINARY = 0; #[elif: os == linux] internal const int32 O_RDONLY = 0; internal const int32 O_WRONLY = 1; @@ -74,6 +134,16 @@ internal const int32 O_CREAT = 64; internal const int32 O_TRUNC = 512; internal const int32 O_APPEND = 1024; internal const int32 O_CLOEXEC = 524288; +internal const int32 O_BINARY = 0; +#[elif: os == windows] +internal const int32 O_RDONLY = 0; +internal const int32 O_WRONLY = 1; +internal const int32 O_RDWR = 2; +internal const int32 O_APPEND = 8; +internal const int32 O_CREAT = 256; +internal const int32 O_TRUNC = 512; +internal const int32 O_CLOEXEC = 0; +internal const int32 O_BINARY = 32768; #[else] internal const int32 O_RDONLY = 0; internal const int32 O_WRONLY = 1; @@ -82,6 +152,7 @@ internal const int32 O_CREAT = 64; internal const int32 O_TRUNC = 512; internal const int32 O_APPEND = 1024; internal const int32 O_CLOEXEC = 524288; +internal const int32 O_BINARY = 0; #[end] // these four, and EBADF, agree on every platform this compiler targets diff --git a/stdlib/std/math/constants.eco b/stdlib/std/math/constants.eco index 6c45cfb6..032c8be6 100644 --- a/stdlib/std/math/constants.eco +++ b/stdlib/std/math/constants.eco @@ -66,3 +66,29 @@ public const float LN_10_F32 = 2.3025851f; */ public const float64 SQRT_2 = 1.4142135623730951; public const float SQRT_2_F32 = 1.4142135f; + +// ---------------------------------------------------------------------------- +// integer extrema +// ---------------------------------------------------------------------------- + +/** + * The largest value of each integer width. `MIN_*` is written as `-MAX - 1` so the + * magnitude never has to be a literal the width cannot hold. + */ +public const int8 MAX_INT8 = 127; +public const int8 MIN_INT8 = -MAX_INT8 - 1; +public const int16 MAX_INT16 = 32767; +public const int16 MIN_INT16 = -MAX_INT16 - 1; +public const int32 MAX_INT32 = 2147483647; +public const int32 MIN_INT32 = -MAX_INT32 - 1; +public const int64 MAX_INT64 = 9223372036854775807; +public const int64 MIN_INT64 = -MAX_INT64 - 1; + +public const uint8 MAX_UINT8 = 255; +public const uint16 MAX_UINT16 = 65535; +public const uint32 MAX_UINT32 = 4294967295; +public const uint64 MAX_UINT64 = 18446744073709551615; + +public const isize MAX_ISIZE = MAX_INT64 as isize; +public const isize MIN_ISIZE = -MAX_ISIZE - 1; +public const usize MAX_USIZE = MAX_UINT64 as usize; diff --git a/stdlib/std/thread/libc.eco b/stdlib/std/thread/libc.eco index 0ba88235..5e60204a 100644 --- a/stdlib/std/thread/libc.eco +++ b/stdlib/std/thread/libc.eco @@ -12,6 +12,7 @@ namespace std::thread; +#[if: os != windows] internal extern { function pthread_create as c_pthread_create( ptr $thread, @@ -38,9 +39,170 @@ internal extern { function pthread_cond_broadcast as c_pthread_cond_broadcast(ptr $cond) : int32; function sched_yield as c_sched_yield() : int32; - function usleep as c_usleep(uint32 $usec) : int32; function sysconf as c_sysconf(int32 $name) : int64; } +#[end] + +#[if: os == windows] +internal extern { + function CreateThread as c_CreateThread( + ptr $attr, + usize $stack, + extern function(ptr)> $start, + ptr $arg, + uint32 $flags, + ptr $tid + ) : ptr; + + function WaitForSingleObject as c_WaitForSingleObject(ptr $handle, uint32 $ms) : uint32; + function CloseHandle as c_CloseHandle(ptr $handle) : int32; + function GetCurrentThreadId as c_GetCurrentThreadId() : uint32; + function SwitchToThread as c_SwitchToThread() : int32; + function GetActiveProcessorCount as c_GetActiveProcessorCount(uint16 $group) : uint32; + + function InitializeSRWLock as c_InitializeSRWLock(ptr $lock) : void; + function AcquireSRWLockExclusive as c_AcquireSRWLockExclusive(ptr $lock) : void; + function ReleaseSRWLockExclusive as c_ReleaseSRWLockExclusive(ptr $lock) : void; + function TryAcquireSRWLockExclusive as c_TryAcquireSRWLockExclusive(ptr $lock) : uint8; + + function InitializeConditionVariable as c_InitializeConditionVariable(ptr $cond) : void; + function SleepConditionVariableSRW as c_SleepConditionVariableSRW( + ptr $cond, ptr $lock, uint32 $ms, uint32 $flags) : int32; + function WakeConditionVariable as c_WakeConditionVariable(ptr $cond) : void; + function WakeAllConditionVariable as c_WakeAllConditionVariable(ptr $cond) : void; +} + +internal const uint32 WIN_INFINITE = 4294967295; +internal const uint16 WIN_ALL_PROCESSOR_GROUPS = 65535; + +internal function c_pthread_create( + ptr $thread, + ptr $attr, + extern function(ptr)> $start, + ptr $arg +) : int32 +{ + uint32 $tid = 0; + ptr $handle = c_CreateThread(ptr(null), 0, $start, $arg, 0, &$tid); + + if ($handle:$ == null) { + return ENOMEM; + } + + usize $bits = 0; + mem::copy(ptr(&$bits), ptr(&$handle), mem::size>()); + $thread:$[0] = $bits; + return 0; +} + +internal function c_pthread_join(usize $thread, ptr> $retval) : int32 +{ + usize $bits = $thread; + ptr $handle = ptr(null); + mem::copy(ptr(&$handle), ptr(&$bits), mem::size>()); + c_WaitForSingleObject($handle, WIN_INFINITE); + c_CloseHandle($handle); + return 0; +} + +internal function c_pthread_detach(usize $thread) : int32 +{ + usize $bits = $thread; + ptr $handle = ptr(null); + mem::copy(ptr(&$handle), ptr(&$bits), mem::size>()); + c_CloseHandle($handle); + return 0; +} + +internal function c_pthread_self() : usize +{ + return c_GetCurrentThreadId() as usize; +} + +internal function c_pthread_equal(usize $a, usize $b) : int32 +{ + if ($a == $b) { + return 1; + } + + return 0; +} + +internal function c_pthread_mutex_init(ptr $mutex, ptr $attr) : int32 +{ + c_InitializeSRWLock($mutex); + return 0; +} + +internal function c_pthread_mutex_destroy(ptr $mutex) : int32 +{ + return 0; +} + +internal function c_pthread_mutex_lock(ptr $mutex) : int32 +{ + c_AcquireSRWLockExclusive($mutex); + return 0; +} + +internal function c_pthread_mutex_trylock(ptr $mutex) : int32 +{ + if (c_TryAcquireSRWLockExclusive($mutex) != 0) { + return 0; + } + + return EBUSY; +} + +internal function c_pthread_mutex_unlock(ptr $mutex) : int32 +{ + c_ReleaseSRWLockExclusive($mutex); + return 0; +} + +internal function c_pthread_cond_init(ptr $cond, ptr $attr) : int32 +{ + c_InitializeConditionVariable($cond); + return 0; +} + +internal function c_pthread_cond_destroy(ptr $cond) : int32 +{ + return 0; +} + +internal function c_pthread_cond_wait(ptr $cond, ptr $mutex) : int32 +{ + if (c_SleepConditionVariableSRW($cond, $mutex, WIN_INFINITE, 0) == 0) { + return EDEADLK; + } + + return 0; +} + +internal function c_pthread_cond_signal(ptr $cond) : int32 +{ + c_WakeConditionVariable($cond); + return 0; +} + +internal function c_pthread_cond_broadcast(ptr $cond) : int32 +{ + c_WakeAllConditionVariable($cond); + return 0; +} + +internal function c_sched_yield() : int32 +{ + c_SwitchToThread(); + return 0; +} + +internal function c_sysconf(int32 $name) : int64 +{ + return c_GetActiveProcessorCount(WIN_ALL_PROCESSOR_GROUPS) as int64; +} +#[end] internal const int32 ENOMEM = 12; internal const int32 EBUSY = 16; diff --git a/stdlib/std/thread/thread.eco b/stdlib/std/thread/thread.eco index 9dadf0a8..888cad5e 100644 --- a/stdlib/std/thread/thread.eco +++ b/stdlib/std/thread/thread.eco @@ -121,23 +121,20 @@ public function yield() : void } /** - * Sleeps `$ms` milliseconds, chopped into pieces of at most 500 so a long sleep - * is still interruptible by the platform's usleep limit. + * Sleeps `$ms` milliseconds. The same call as `std::time::sleep`, in milliseconds. + * + * `$ms as int64` wraps if `$ms` is above int64's maximum. That is a sleep of 292 million + * years; saturate rather than becoming a negative duration and a no-op. */ public function sleep(usize $ms) : void { - usize $left = $ms; - - while ($left > 0) { - usize $chunk = $left; + int64 $n = std::math::MAX_INT64; - if ($chunk > 500) { - $chunk = 500; - } - - c_usleep(($chunk * 1000) as uint32); - $left = $left - $chunk; + if ($ms <= std::math::MAX_INT64 as usize) { + $n = $ms as int64; } + + std::time::sleep(std::time::duration::millis($n)); } /** diff --git a/stdlib/std/time/duration.eco b/stdlib/std/time/duration.eco new file mode 100644 index 00000000..9195c036 --- /dev/null +++ b/stdlib/std/time/duration.eco @@ -0,0 +1,246 @@ +/** + * A signed length of time, not a moment. + * + * Two words, matching C's `timespec`: `$secs` and `$nanos`. Not a single nanosecond count, so a + * clock read and a `nanosleep` take the value as-is, comparison is lexicographic with no sign + * arm, and `as_millis()` is one floor-correct expression even for negatives. + * + * The invariant: `$nanos` is in `[0, 999999999]` and `$secs` carries the sign. `-1.5s` is + * `{secs: -2, nanos: 500000000}`, timespec/floor form, never `{secs: -1, nanos: -500000000}`. + * Runtime arithmetic wraps, so normalisation is an explicit div/mod plus one borrow branch. + * + * Fields are `internal const`. `private` would refuse the free operators in this file; unmarked + * would publish a mutable `$nanos` and the constructor's fold would be optional. The handwritten + * constructor has the field-wise signature, so it suppresses the free one: nothing outside this + * type can skip normalisation by writing `duration($secs, $nanos)` with a wild `$nanos`. + */ + +namespace std::time; + +internal const int64 NANOS_PER_SEC = 1000000000; +internal const int64 NANOS_PER_MILLI = 1000000; +internal const int64 NANOS_PER_MICRO = 1000; +internal const int64 MILLIS_PER_SEC = 1000; +internal const int64 MICROS_PER_SEC = 1000000; +internal const int64 SECS_PER_MINUTE = 60; +internal const int64 SECS_PER_HOUR = 3600; + +public struct duration +{ + internal const int64 $secs; + internal const int64 $nanos; + + // `const int64` matches the field-wise signature of `internal const` properties. + // `int64` alone would leave both constructors live and every call ambiguous. + constructor(const int64 $secs, const int64 $nanos) + { + int64 $extra = $nanos / NANOS_PER_SEC; + int64 $rem = $nanos % NANOS_PER_SEC; + + if ($rem < 0) { + $rem = $rem + NANOS_PER_SEC; + $extra = $extra - 1; + } + + $this->secs = $secs + $extra; + $this->nanos = $rem; + } + + #[inline] + public static function zero() : duration + { + return duration(0, 0); + } + + #[inline] + public static function secs(int64 $n) : duration + { + return duration($n, 0); + } + + #[inline] + public static function millis(int64 $n) : duration + { + return duration($n / MILLIS_PER_SEC, ($n % MILLIS_PER_SEC) * NANOS_PER_MILLI); + } + + #[inline] + public static function micros(int64 $n) : duration + { + return duration($n / MICROS_PER_SEC, ($n % MICROS_PER_SEC) * NANOS_PER_MICRO); + } + + #[inline] + public static function nanos(int64 $n) : duration + { + return duration(0, $n); + } + + #[inline] + public static function mins(int64 $n) : duration + { + return duration::secs($n * SECS_PER_MINUTE); + } + + #[inline] + public static function hours(int64 $n) : duration + { + return duration::secs($n * SECS_PER_HOUR); + } + + /** + * Whole seconds, rounding toward negative infinity. `-1.5s` answers `-2`. + */ + #[inline] + const function as_secs() : int64 + { + return $this->secs; + } + + #[inline] + const function subsec_nanos() : int32 + { + return $this->nanos as int32; + } + + /** + * Floor-correct for negatives: `secs * MILLIS_PER_SEC + nanos / NANOS_PER_MILLI`. + */ + #[inline] + const function as_millis() : int64 + { + return $this->secs * MILLIS_PER_SEC + $this->nanos / NANOS_PER_MILLI; + } + + #[inline] + const function as_micros() : int64 + { + return $this->secs * MICROS_PER_SEC + $this->nanos / NANOS_PER_MICRO; + } + + /** + * Whole nanoseconds. Wraps past about plus or minus 292 years. + */ + #[inline] + const function as_nanos() : int64 + { + return $this->secs * NANOS_PER_SEC + $this->nanos; + } + + #[inline] + const function as_float_secs() : float64 + { + return ($this->secs as float64) + ($this->nanos as float64) / (NANOS_PER_SEC as float64); + } + + #[inline] + const function is_zero() : bool + { + return $this->secs == 0 && $this->nanos == 0; + } + + /** + * `secs < 0`. Sound under the invariant: a negative length always has a negative `$secs`. + */ + #[inline] + const function is_negative() : bool + { + return $this->secs < 0; + } + + const function abs() : duration + { + if ($this->secs < 0) { + return duration(-$this->secs, -$this->nanos); + } + + return duration($this->secs, $this->nanos); + } +} + +operator (duration $a) + (duration $b) : duration +{ + return duration($a->secs + $b->secs, $a->nanos + $b->nanos); +} + +operator (duration $a) - (duration $b) : duration +{ + return duration($a->secs - $b->secs, $a->nanos - $b->nanos); +} + +operator -(duration $a) : duration +{ + return duration(-$a->secs, -$a->nanos); +} + +operator (duration $a) == (duration $b) : bool +{ + return $a->secs == $b->secs && $a->nanos == $b->nanos; +} + +operator (duration $a) != (duration $b) : bool +{ + if ($a == $b) { + return false; + } + + return true; +} + +operator (duration $a) < (duration $b) : bool +{ + if ($a->secs != $b->secs) { + return $a->secs < $b->secs; + } + + return $a->nanos < $b->nanos; +} + +operator (duration $a) > (duration $b) : bool +{ + return $b < $a; +} + +operator (duration $a) <= (duration $b) : bool +{ + if ($a < $b) { + return true; + } + + return $a == $b; +} + +operator (duration $a) >= (duration $b) : bool +{ + if ($b < $a) { + return true; + } + + return $a == $b; +} + +/** + * Whole-nanosecond product. Only meaningful within about plus or minus 292 years, the + * same bound as `as_nanos()`. + */ +operator (duration $a) * (int64 $n) : duration +{ + return duration::nanos($a->as_nanos() * $n); +} + +operator (int64 $n) * (duration $a) : duration +{ + return $a * $n; +} + +/** + * Truncating whole-nanosecond division. Same 292-year bound as `as_nanos()`. + */ +operator (duration $a) / (int64 $n) : duration +{ + if ($n == 0) { + die('duration divided by zero'); + } + + return duration::nanos($a->as_nanos() / $n); +} diff --git a/stdlib/std/time/from.eco b/stdlib/std/time/from.eco new file mode 100644 index 00000000..d4771848 --- /dev/null +++ b/stdlib/std/time/from.eco @@ -0,0 +1,111 @@ +/** + * `str::from` for `duration`, so `"{$d}"` is Go-style (`1h2m3.5s`). + * + * A file of its own because a namespace is a property of the file: duration.eco is `std::time` + * and cannot also be `str`. Spec-less only: `"{$d:>8}"` fails by name, which is intended. + * + * Negating int64-min seconds wraps. Unreachable at that magnitude; not a branch. + */ + +namespace str; + +private const int32 MILLI_FRAC_DIGITS = 6; +private const int32 MICRO_FRAC_DIGITS = 3; +private const int32 NANO_FRAC_DIGITS = 9; + +public function from(const std::time::duration& $d) : string +{ + if ($d->is_zero()) { + return '0s'; + } + + if ($d->is_negative()) { + return concat('-', render_duration($d->abs())); + } + + return render_duration($d); +} + +private function render_duration(const std::time::duration& $d) : string +{ + int64 $secs = $d->as_secs(); + int32 $nanos = $d->subsec_nanos(); + + if ($secs == 0) { + if ($nanos >= std::time::NANOS_PER_MILLI) { + return format_unit( + ($nanos / std::time::NANOS_PER_MILLI) as uint64, + ($nanos % std::time::NANOS_PER_MILLI) as uint64, + MILLI_FRAC_DIGITS, + 'ms' + ); + } + + if ($nanos >= std::time::NANOS_PER_MICRO) { + return format_unit( + ($nanos / std::time::NANOS_PER_MICRO) as uint64, + ($nanos % std::time::NANOS_PER_MICRO) as uint64, + MICRO_FRAC_DIGITS, + 'us' + ); + } + + return concat(from($nanos as uint64), 'ns'); + } + + int64 $hours = $secs / std::time::SECS_PER_HOUR; + int64 $mins = ($secs % std::time::SECS_PER_HOUR) / std::time::SECS_PER_MINUTE; + int64 $s = $secs % std::time::SECS_PER_MINUTE; + + string $out = ''; + + if ($hours != 0) { + $out = concat(from($hours), 'h'); + } + + if ($hours != 0 || $mins != 0) { + $out = concat($out, from($mins)); + $out = concat($out, 'm'); + } + + $out = concat($out, from($s)); + + if ($nanos != 0) { + $out = concat($out, '.'); + $out = concat($out, frac_digits($nanos as uint64, NANO_FRAC_DIGITS)); + } + + return concat($out, 's'); +} + +private function format_unit(uint64 $whole, uint64 $frac, int32 $width, const string& $unit) : string +{ + if ($frac == 0) { + return "{$whole}{$unit}"; + } + + string $digits = frac_digits($frac, $width); + return "{$whole}.{$digits}{$unit}"; +} + +private function frac_digits(uint64 $frac, int32 $width) : string +{ + string $s = from($frac); + usize $need = $width as usize; + + while ($s->size() < $need) { + $s = concat('0', $s); + } + + usize $end = $s->size(); + + while ($end > 0) { + if ($s->byte($end - 1) != CH_ZERO) { + break; + } + + $end = $end - 1; + } + + return $s->sub(0, $end); +} diff --git a/stdlib/std/time/instant.eco b/stdlib/std/time/instant.eco new file mode 100644 index 00000000..499c5c7f --- /dev/null +++ b/stdlib/std/time/instant.eco @@ -0,0 +1,87 @@ +/** + * A moment on the monotonic clock. + * + * `$raw` is time since an arbitrary fixed origin. Subtract two of them, or add a duration; + * do not print one. `instant::now()` is `clock_gettime(CLOCK_MONOTONIC)` (QPC on Windows). + */ + +namespace std::time; + +public struct instant +{ + internal const duration $raw; + + public static function now() : instant + { + timespec $ts = timespec(0, 0); + c_clock_gettime(CLOCK_MONOTONIC, &$ts); + return instant(duration($ts->sec, $ts->nsec)); + } + + const function elapsed() : duration + { + return instant::now() - $this; + } + + const function since(instant $earlier) : duration + { + return $this - $earlier; + } +} + +operator (instant $a) - (instant $b) : duration +{ + return $a->raw - $b->raw; +} + +operator (instant $a) + (duration $d) : instant +{ + return instant($a->raw + $d); +} + +operator (instant $a) - (duration $d) : instant +{ + return instant($a->raw - $d); +} + +operator (instant $a) == (instant $b) : bool +{ + return $a->raw == $b->raw; +} + +operator (instant $a) != (instant $b) : bool +{ + if ($a == $b) { + return false; + } + + return true; +} + +operator (instant $a) < (instant $b) : bool +{ + return $a->raw < $b->raw; +} + +operator (instant $a) > (instant $b) : bool +{ + return $b < $a; +} + +operator (instant $a) <= (instant $b) : bool +{ + if ($a < $b) { + return true; + } + + return $a == $b; +} + +operator (instant $a) >= (instant $b) : bool +{ + if ($b < $a) { + return true; + } + + return $a == $b; +} diff --git a/stdlib/std/time/libc.eco b/stdlib/std/time/libc.eco new file mode 100644 index 00000000..7d9f4ce6 --- /dev/null +++ b/stdlib/std/time/libc.eco @@ -0,0 +1,127 @@ +/** + * Platform clock and sleep bindings. + * + * POSIX is `clock_gettime` / `nanosleep` with a `timespec` that is also this module's duration + * split, so a clock read and a sleep take the same two words with no conversion. Windows has + * neither call; the shims below keep those two names so instant.eco, timestamp.eco and sleep.eco + * never write a platform conditional. + * + * `CLOCK_MONOTONIC` rather than `_RAW` on both Unixes: NTP may slew it, it never steps, and that + * is the clock Go and Rust hand you for measuring. Darwin's value is 6; everyone else uses 1, + * which is the linux uapi and the id the Windows QPC shim keys on. + * + * A C symbol may only be declared once per module, so they live here. `internal`: the rest of + * `std::time` names them, a consumer does not. + */ + +namespace std::time; + +/** + * C's `struct timespec` on every Unix we build for: two 64-bit fields, `time_t` then `long`. + * Windows has no such type; the shims still write one, as a plain Echo struct of the same shape. + * + * libc *writes* into it, so both fields are declared exactly. A binding that only read could + * leave the tail off; this one cannot. + */ +internal struct timespec +{ + int64 $sec; + int64 $nsec; +} + +#[if: os != windows] +internal extern { + function clock_gettime as c_clock_gettime(int32 $clock, ptr $ts) : int32; + function nanosleep as c_nanosleep(ptr $req, ptr $rem) : int32; +} +#[end] + +internal const int32 CLOCK_REALTIME = 0; + +#[if: os == darwin] +internal const int32 CLOCK_MONOTONIC = 6; +#[else] +internal const int32 CLOCK_MONOTONIC = 1; +#[end] + +#[if: os == windows] +internal extern { + function QueryPerformanceCounter as c_QueryPerformanceCounter(ptr $count) : int32; + function QueryPerformanceFrequency as c_QueryPerformanceFrequency(ptr $freq) : int32; + function GetSystemTimePreciseAsFileTime as c_GetSystemTimePreciseAsFileTime(ptr $time) : void; + function Sleep as c_Sleep(uint32 $ms) : void; +} + +internal const int64 FILETIME_EPOCH_DELTA_SECS = 11644473600; +internal const int64 FILETIME_TICKS_PER_SEC = 10000000; +internal const int64 FILETIME_NANOS_PER_TICK = 100; +internal const int64 SLEEP_CHUNK_MS = 4000000000; + +private function read_qpf() : int64 +{ + int64 $f = 0; + c_QueryPerformanceFrequency(&$f); + + if ($f == 0) { + die('QueryPerformanceFrequency returned 0'); + } + + return $f; +} + +// QPF is boot-constant. The static is lazy, once, and thread-safe: the language rule, not +// a comment. A dummy field so the type has a constructor; nothing constructs one. +private struct qpc +{ + int32 $pad; + + static int64 $freq = read_qpf(); +} + +internal function c_clock_gettime(int32 $clock, ptr $ts) : int32 +{ + if ($clock == CLOCK_MONOTONIC) { + int64 $count = 0; + c_QueryPerformanceCounter(&$count); + + int64 $f = qpc::$freq; + $ts->sec = $count / $f; + $ts->nsec = (($count % $f) * NANOS_PER_SEC) / $f; + return 0; + } + + uint64 $ticks = 0; + c_GetSystemTimePreciseAsFileTime(&$ticks); + + uint64 $per_sec = FILETIME_TICKS_PER_SEC as uint64; + int64 $secs_1601 = ($ticks / $per_sec) as int64; + int64 $subsec = ($ticks % $per_sec) as int64; + $ts->sec = $secs_1601 - FILETIME_EPOCH_DELTA_SECS; + $ts->nsec = $subsec * FILETIME_NANOS_PER_TICK; + return 0; +} + +internal function c_nanosleep(ptr $req, ptr $rem) : int32 +{ + int64 $ms = $req->sec * MILLIS_PER_SEC + ($req->nsec + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI; + + if ($ms < 0) { + $ms = 0; + } + + while ($ms > 0) { + int64 $chunk = $ms; + + if ($chunk > SLEEP_CHUNK_MS) { + $chunk = SLEEP_CHUNK_MS; + } + + c_Sleep($chunk as uint32); + $ms = $ms - $chunk; + } + + $rem->sec = 0; + $rem->nsec = 0; + return 0; +} +#[end] diff --git a/stdlib/std/time/sleep.eco b/stdlib/std/time/sleep.eco new file mode 100644 index 00000000..fcff3ae3 --- /dev/null +++ b/stdlib/std/time/sleep.eco @@ -0,0 +1,37 @@ +/** + * Sleep for a duration. + * + * A non-positive duration returns immediately. Negative sleep is a no-op, matching Rust and Go. + * The duration is already a timespec, so the body is a loop over `nanosleep` that retries on + * EINTR from the remainder. `$rem` is zeroed before every call: a non-EINTR failure leaves it + * zero and that is the exit, a proof of progress with no errno binding. + * + * Windows rounds the wait up to the next millisecond and chunks at four billion ms, the + * `uint32` Sleep boundary. There is no 500 ms chopping on Unix; that was `usleep`'s limit. + */ + +namespace std::time; + +public function sleep(duration $d) : void +{ + if ($d <= duration::zero()) { + return; + } + + timespec $req = timespec($d->secs, $d->nanos); + + while (true) { + timespec $rem = timespec(0, 0); + int32 $rc = c_nanosleep(&$req, &$rem); + + if ($rc == 0) { + return; + } + + if ($rem->sec == 0 && $rem->nsec == 0) { + return; + } + + $req = $rem; + } +} diff --git a/stdlib/std/time/timestamp.eco b/stdlib/std/time/timestamp.eco new file mode 100644 index 00000000..c8d8f7c1 --- /dev/null +++ b/stdlib/std/time/timestamp.eco @@ -0,0 +1,120 @@ +/** + * A moment on the wall clock: a signed duration since 1970-01-01T00:00:00Z. + * + * No calendar, no timezone, no `str::from`. Human rendering is a later layer; a bare-number + * format of this type would be kept forever, so there is none. `timestamp::now()` is + * `clock_gettime(CLOCK_REALTIME)` (GetSystemTimePreciseAsFileTime on Windows). + */ + +namespace std::time; + +public struct timestamp +{ + internal const duration $raw; + + public static function now() : timestamp + { + timespec $ts = timespec(0, 0); + c_clock_gettime(CLOCK_REALTIME, &$ts); + return timestamp(duration($ts->sec, $ts->nsec)); + } + + #[inline] + public static function from_unix_secs(int64 $secs) : timestamp + { + return timestamp(duration::secs($secs)); + } + + #[inline] + public static function from_unix_millis(int64 $millis) : timestamp + { + return timestamp(duration::millis($millis)); + } + + #[inline] + const function since_epoch() : duration + { + return $this->raw; + } + + /** + * Whole Unix seconds, rounding toward negative infinity. Pre-1970 rounds down. + */ + #[inline] + const function unix_secs() : int64 + { + return $this->raw->as_secs(); + } + + #[inline] + const function unix_millis() : int64 + { + return $this->raw->as_millis(); + } + + /** + * Whole Unix nanoseconds. Wraps in 2262. + */ + #[inline] + const function unix_nanos() : int64 + { + return $this->raw->as_nanos(); + } +} + +operator (timestamp $a) - (timestamp $b) : duration +{ + return $a->raw - $b->raw; +} + +operator (timestamp $a) + (duration $d) : timestamp +{ + return timestamp($a->raw + $d); +} + +operator (timestamp $a) - (duration $d) : timestamp +{ + return timestamp($a->raw - $d); +} + +operator (timestamp $a) == (timestamp $b) : bool +{ + return $a->raw == $b->raw; +} + +operator (timestamp $a) != (timestamp $b) : bool +{ + if ($a == $b) { + return false; + } + + return true; +} + +operator (timestamp $a) < (timestamp $b) : bool +{ + return $a->raw < $b->raw; +} + +operator (timestamp $a) > (timestamp $b) : bool +{ + return $b < $a; +} + +operator (timestamp $a) <= (timestamp $b) : bool +{ + if ($a < $b) { + return true; + } + + return $a == $b; +} + +operator (timestamp $a) >= (timestamp $b) : bool +{ + if ($b < $a) { + return true; + } + + return $a == $b; +} diff --git a/tests/ast_module_embedder.cpp b/tests/ast_module_embedder.cpp new file mode 100644 index 00000000..b770f197 --- /dev/null +++ b/tests/ast_module_embedder.cpp @@ -0,0 +1,70 @@ +#include + +#include +#include + +#include +#include +#include +#include + +// AST::embedded_source_path is the sole owner of the path written into +// stdlib_embedded.h. the Windows release regenerates that header on the runner, +// and `path.string()` there is `D:\a\echo\echo\stdlib\core\ordered_map.eco` - +// a C string whose `\o` and `\u` are compile errors, not a path + +namespace fs = std::filesystem; + +namespace +{ + +std::string with_backslashes(std::string path) +{ + for (char &c : path) { + if (c == '/') { + c = '\\'; + } + } + return path; +} + +std::string read_file(const fs::path &path) +{ + std::ifstream in(path); + REQUIRE(in.is_open()); + return std::string(std::istreambuf_iterator(in), std::istreambuf_iterator()); +} + +}; + +TEST_CASE("a stdlib source is rewritten to a virtual generic path", "[embedder]") +{ + const fs::path native = fs::path(STDLIB_SOURCE_DIR) / "core" / "ordered_map.eco"; + REQUIRE(AST::embedded_source_path(native) == "stdlib:/core/ordered_map.eco"); +} + +TEST_CASE("native backslashes still rewrite to stdlib: with forward slashes", "[embedder]") +{ + // the windows bug, host-independent: cmake's STDLIB_SOURCE_DIR uses `/`, + // filesystem::path::string() on Windows uses `\`, and the prefix match + // failed so the raw native path was written into a C string + const std::string native = with_backslashes( + (fs::path(STDLIB_SOURCE_DIR) / "core" / "utf8.eco").generic_string()); + REQUIRE(AST::embedded_source_path(native) == "stdlib:/core/utf8.eco"); +} + +TEST_CASE("write_embedded_module emits a generic add_file path as a C string", "[embedder]") +{ + AST::Module module("stdlib", 0); + auto &file = module.add_file(fs::path(STDLIB_SOURCE_DIR) / "core" / "ordered_map.eco"); + file.set_content("function f() {}\n"); + + const fs::path tmp_dir = fs::path(ECO_E2E_TMP_DIR) / "embedder"; + fs::create_directories(tmp_dir); + const fs::path tmp = tmp_dir / "stdlib_embedded.h"; + AST::write_embedded_module(module, tmp.string()); + + const std::string contents = read_file(tmp); + REQUIRE(contents.find("module.add_file(\"stdlib:/core/ordered_map.eco\")") != std::string::npos); + REQUIRE(contents.find('\\') == std::string::npos); +} diff --git a/tests/build_layout.cpp b/tests/build_layout.cpp index 43377fd5..8f5f575f 100644 --- a/tests/build_layout.cpp +++ b/tests/build_layout.cpp @@ -171,7 +171,7 @@ TEST_CASE("a manifest may name its own build directory", "[layout]") } } -TEST_CASE("--build-dir outranks the manifest", "[layout]") +TEST_CASE("`--build-dir` outranks the manifest", "[layout]") { ScopedProject project("flag_wins"); @@ -207,7 +207,11 @@ TEST_CASE("a build leaves nothing beside the binary it produced", "[layout]") REQUIRE(project.echoc("build -o out", project.root()).exit_code == 0); +#if defined(_WIN32) + REQUIRE(entries_of(project.root()) == std::set{ "module.eco", "src", "out.exe", "ecobuild" }); +#else REQUIRE(entries_of(project.root()) == std::set{ "module.eco", "src", "out", "ecobuild" }); +#endif } TEST_CASE("a program with no project leaves only its binary", "[layout]") @@ -221,7 +225,11 @@ TEST_CASE("a program with no project leaves only its binary", "[layout]") REQUIRE(project.echoc("build -o out x.eco", project.root()).exit_code == 0); +#if defined(_WIN32) + REQUIRE(entries_of(project.root()) == std::set{ "x.eco", "out.exe" }); +#else REQUIRE(entries_of(project.root()) == std::set{ "x.eco", "out" }); +#endif } TEST_CASE("a build directory holding somebody else's work is refused", "[layout]") diff --git a/tests/build_targets.cpp b/tests/build_targets.cpp index 8c2da89a..46fc6978 100644 --- a/tests/build_targets.cpp +++ b/tests/build_targets.cpp @@ -73,10 +73,8 @@ TEST_CASE("each target runs its own entry file and nothing of the other's", "[ta REQUIRE(project.echoc("build").exit_code == 0); - const ProcessResult clock = EchoTests::run_capturing( - EchoTests::quoted(project.root() / "ecobuild/clock") + " 2>&1"); - const ProcessResult serve = EchoTests::run_capturing( - EchoTests::quoted(project.root() / "ecobuild/serve") + " 2>&1"); + const ProcessResult clock = EchoTests::run_binary(project.root() / "ecobuild/clock"); + const ProcessResult serve = EchoTests::run_binary(project.root() / "ecobuild/serve"); INFO("clock: " << clock.output << "\nserve: " << serve.output); @@ -87,7 +85,7 @@ TEST_CASE("each target runs its own entry file and nothing of the other's", "[ta REQUIRE(serve.output.find("CLOCK") == std::string::npos); } -TEST_CASE("--target builds the one named and leaves the others alone", "[targets]") +TEST_CASE("`--target` builds the one named and leaves the others alone", "[targets]") { ScopedProject project("one_target_only"); write_two_target_project(project); @@ -100,7 +98,7 @@ TEST_CASE("--target builds the one named and leaves the others alone", "[targets REQUIRE_FALSE(EchoTests::file_exists(project.root() / "ecobuild/serve")); } -TEST_CASE("-o overrides where one target's binary goes", "[targets]") +TEST_CASE("`-o` overrides where one target's binary goes", "[targets]") { ScopedProject project("output_override"); write_two_target_project(project); @@ -256,8 +254,7 @@ TEST_CASE("a dependency's targets are its own and reach no consumer", "[targets] REQUIRE_FALSE(EchoTests::file_exists(project.root() / "app/ecobuild/tool")); REQUIRE_FALSE(EchoTests::file_exists(project.root() / "lib/ecobuild/tool")); - const ProcessResult ran = EchoTests::run_capturing( - EchoTests::quoted(project.root() / "app/ecobuild/app") + " 2>&1"); + const ProcessResult ran = EchoTests::run_binary(project.root() / "app/ecobuild/app"); INFO(ran.output); REQUIRE(ran.output.find("HELLO") != std::string::npos); diff --git a/tests/c_build.cpp b/tests/c_build.cpp index 3c97d46c..ba17bf64 100644 --- a/tests/c_build.cpp +++ b/tests/c_build.cpp @@ -134,6 +134,24 @@ TEST_CASE("an unknown C build scheme is refused", "[cbuild]") REQUIRE(refusal.find("sources, include, define, flag") != std::string::npos); } +TEST_CASE("coff_exports_from_nm keeps defined text and data", "[cbuild]") +{ + const std::string nm = + "shim.o:\n" + "00000000 T eco_shim_answer\n" + "00000004 D eco_shim_flag\n" + "00000008 B eco_shim_bss\n" + "00000000 R eco_shim_ro\n" + " U printf\n" + "00000010 T ?mangled@@YAXXZ\n" + "00000020 T .text\n"; + + const std::vector names = Compiler::coff_exports_from_nm(nm); + + REQUIRE(names == std::vector{ + "eco_shim_answer", "eco_shim_flag", "eco_shim_bss", "eco_shim_ro" }); +} + TEST_CASE("a C object is reused when nothing changed", "[cbuild][cache]") { ScopedProject project("reuse"); diff --git a/tests/check_directives.cpp b/tests/check_directives.cpp new file mode 100644 index 00000000..209d757a --- /dev/null +++ b/tests/check_directives.cpp @@ -0,0 +1,79 @@ +#include + +#include +#include + +#include "eco_check_directives.h" + +using EchoTests::CheckDirective; +using EchoTests::apply_check_directives; +using EchoTests::parse_check_directives; + +namespace +{ + std::vector parse_or_fail(const std::string &body) + { + std::vector directives; + std::string error; + REQUIRE(parse_check_directives(body, 1, "t.test", directives, error)); + REQUIRE(error.empty()); + return directives; + } +}; + +TEST_CASE("host-gated CHECK is skipped on other hosts", "[check]") +{ + const auto directives = parse_or_fail( + "CHECK: start\n" + "CHECK-WINDOWS: win-only\n" + "CHECK: end\n"); + + REQUIRE(directives.size() == 3); + CHECK(directives[0].host_os.empty()); + CHECK(directives[1].host_os == "windows"); + CHECK_FALSE(directives[1].negated); + CHECK(directives[2].host_os.empty()); + + const std::string both = "start\nend\n"; + CHECK(apply_check_directives(directives, both, "linux") == ""); + CHECK(apply_check_directives(directives, both, "windows").find("never matched") + != std::string::npos); + + const std::string windows = "start\nwin-only\nend\n"; + CHECK(apply_check_directives(directives, windows, "windows") == ""); + CHECK(apply_check_directives(directives, windows, "linux") == ""); +} + +TEST_CASE("host-gated CHECK-NOT is skipped on other hosts", "[check]") +{ + const auto directives = parse_or_fail( + "CHECK: start\n" + "CHECK-NOT-WINDOWS: forbidden\n" + "CHECK: end\n"); + + REQUIRE(directives.size() == 3); + CHECK(directives[1].host_os == "windows"); + CHECK(directives[1].negated); + + const std::string hay = "start\nforbidden\nend\n"; + CHECK(apply_check_directives(directives, hay, "linux") == ""); + CHECK(apply_check_directives(directives, hay, "windows").find("matched, but must not") + != std::string::npos); +} + +TEST_CASE("unknown host in a CHECK is an error", "[check]") +{ + std::vector directives; + std::string error; + REQUIRE_FALSE(parse_check_directives("CHECK-FREEBSD: foo\n", 1, "t.test", directives, error)); + CHECK(error.find("unknown host 'freebsd'") != std::string::npos); +} + +TEST_CASE("CHECK-NOT still beats CHECK as a prefix", "[check]") +{ + const auto directives = parse_or_fail("CHECK-NOT: absent\nCHECK: present\n"); + REQUIRE(directives.size() == 2); + CHECK(directives[0].negated); + CHECK(directives[0].host_os.empty()); + CHECK_FALSE(directives[1].negated); +} diff --git a/tests/command_line.cpp b/tests/command_line.cpp index fc4cb99c..e73f56c5 100644 --- a/tests/command_line.cpp +++ b/tests/command_line.cpp @@ -14,6 +14,12 @@ #include #include +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#endif + #include "subprocess.h" #include "terminal_fixture.h" @@ -710,6 +716,41 @@ TEST_CASE("the value tree is drawn with the terminal's own repertoire", "[cli]") } } +TEST_CASE("prepare_terminal is safe to call twice", "[cli]") +{ + Compiler::prepare_terminal(); + Compiler::prepare_terminal(); +} + +#if defined(_WIN32) +TEST_CASE("prepare_terminal sets an attached console to UTF-8", "[cli]") +{ + const HANDLE err = GetStdHandle(STD_ERROR_HANDLE); + DWORD mode = 0; + if (err == INVALID_HANDLE_VALUE || err == nullptr || GetConsoleMode(err, &mode) == 0) { + SKIP("stderr is not a console"); + } + + Compiler::prepare_terminal(); + REQUIRE(GetConsoleOutputCP() == CP_UTF8); +} +#endif + +TEST_CASE("a heading is drawn, not shouted", "[cli]") +{ + const std::string unicode = page(Subcommand::t_none, a_terminal(true, 80, false)); + const std::string ascii = page(Subcommand::t_none, TerminalCapabilities::plain()); + + // U+258C LEFT HALF BLOCK, then the word, then the rule of U+2500. The ascii theme draws neither + // glyph, so the heading is the word alone rather than a row of hyphens pretending to be a rule + REQUIRE(contains(unicode, "\u258c Usage ")); + REQUIRE(contains(unicode, "\u2500")); + + REQUIRE(contains(ascii, "Usage\n")); + REQUIRE_FALSE(contains(ascii, "\u258c")); + REQUIRE_FALSE(contains(ascii, "\u2500")); +} + // **summaries form a column that can be read down.** One width per page, so a row that escaped it would // be the row that breaks the whole page TEST_CASE("every summary on a page starts at the same column", "[cli]") @@ -824,7 +865,7 @@ TEST_CASE("a refusal is the sentence, the usage and where to read more", "[cli]" // **the prose is one command away, and the page says so.** The full description was written once, into // the table, and a description nobody can reach is a description nobody wrote -TEST_CASE("--help names one option and prints it in full", "[cli]") +TEST_CASE("`--help` names one option and prints it in full", "[cli]") { CommandLine cli; std::string error; @@ -888,25 +929,33 @@ TEST_CASE("help answers on stdout and a refusal on stderr", "[cli]") { const std::string echoc = EchoTests::quoted(ECHOC_BINARY); - const EchoTests::ProcessResult version = EchoTests::run_capturing(echoc + " --version 2>/dev/null"); +#if defined(_WIN32) + const char *quiet_stdout = " 2>nul"; + const char *stderr_only = " 2>&1 1>nul"; +#else + const char *quiet_stdout = " 2>/dev/null"; + const char *stderr_only = " 2>&1 1>/dev/null"; +#endif + + const EchoTests::ProcessResult version = EchoTests::run_capturing(echoc + " --version" + quiet_stdout); REQUIRE(version.exit_code == 0); REQUIRE(version.output == std::string(ECO_VERSION_STRING) + "\n"); // the page goes to stdout and writes nothing to the diagnostic stream - const EchoTests::ProcessResult help = EchoTests::run_capturing(echoc + " --help 2>/dev/null"); + const EchoTests::ProcessResult help = EchoTests::run_capturing(echoc + " --help" + quiet_stdout); REQUIRE(help.exit_code == 0); REQUIRE(help.output.find("Usage") != std::string::npos); const EchoTests::ProcessResult help_stderr - = EchoTests::run_capturing(echoc + " --help 2>&1 1>/dev/null"); + = EchoTests::run_capturing(echoc + " --help" + stderr_only); REQUIRE(help_stderr.output.empty()); // and a refusal is the mirror of it: stderr, exit 1, nothing on stdout - const EchoTests::ProcessResult bare = EchoTests::run_capturing(echoc + " 2>/dev/null"); + const EchoTests::ProcessResult bare = EchoTests::run_capturing(echoc + quiet_stdout); REQUIRE(bare.exit_code == 1); REQUIRE(bare.output.empty()); const EchoTests::ProcessResult bare_stderr - = EchoTests::run_capturing(echoc + " 2>&1 1>/dev/null"); + = EchoTests::run_capturing(echoc + stderr_only); REQUIRE(bare_stderr.output.find("No command given.") != std::string::npos); } diff --git a/tests/debug_info.cpp b/tests/debug_info.cpp index 0bc315ab..6a59649e 100644 --- a/tests/debug_info.cpp +++ b/tests/debug_info.cpp @@ -95,7 +95,7 @@ echo $c->count + $boxed; REQUIRE(result.output.find("verification failed") == std::string::npos); REQUIRE(result.output.find("definitions differ") == std::string::npos); - const ProcessResult run = run_capturing(quoted(project.root() / "app") + " 2>&1"); + const ProcessResult run = EchoTests::run_binary(project.root() / "app"); INFO(run.output); REQUIRE(run.output.find("47") != std::string::npos); @@ -152,7 +152,7 @@ echo $total; REQUIRE(result.exit_code == 0); } -TEST_CASE("-g is part of the module cache key", "[debuginfo]") +TEST_CASE("`-g` is part of the module cache key", "[debuginfo]") { // an object carrying DWARF is not the object beside it that does not, so serving one for the other is // the unsound-cache case rather than a missed optimization - and it fails silently, because a diff --git a/tests/e2e_eco.cpp b/tests/e2e_eco.cpp index 1ca3c981..c6ed2a53 100644 --- a/tests/e2e_eco.cpp +++ b/tests/e2e_eco.cpp @@ -1,15 +1,18 @@ #include +#include "Compiler/HostTool.h" #include "eco_test_file.h" #include "subprocess.h" #include #include +#include #include #include #include #include #include +#include #include // end-to-end suite @@ -41,7 +44,6 @@ namespace // the shared process primitive - see subprocess.h using EchoTests::ProcessResult; using EchoTests::quoted; - using EchoTests::run_capturing; // `echoc [dump] [flags] `. // @@ -70,40 +72,80 @@ namespace return fs::path(ECO_E2E_TMP_DIR) / fs::relative(eco, root).replace_extension(""); } - std::string echoc_command( - const EchoTests::EcoTestFile &test, const fs::path &eco, const fs::path *binary, - const std::string &dump, const fs::path &root, const fs::path &scratch) + std::vector echoc_argv( + const EchoTests::EcoTestFile &test, + const fs::path &eco, + const fs::path *binary, + const std::string &dump, + const fs::path &root, + const fs::path &scratch + ) { const bool is_build = test.mode == EchoTests::RunMode::t_build; REQUIRE((binary != nullptr) == is_build); - // without --build-dir the default applies, which is `ecobuild` beside each manifest - so - // running the corpus would write artifacts into tests_eco/ and into stdlib/, and two cases - // sharing a manifest would share a cache - // an `args:` line reaches a JIT'd program through echoc's own `--` separator, which is what tells - // the driver where its command line stops and the program's begins. On the `build` path it - // belongs to the binary instead, so it is appended where that is invoked and not here - - // `echoc build -- foo` would be echoc's argument, not the program's - const std::string program_arguments - = (is_build || test.mode == EchoTests::RunMode::t_test || test.arguments.empty()) - ? std::string() - : " --" + test.argument_suffix(); - - // the subcommand, one word off the mode. `test` takes no `-o` and no `--`: it runs no program of the - // case's, so there is nothing for either to be about - const std::string subcommand = is_build - ? "build -o " + quoted(*binary) + " " - : (test.mode == EchoTests::RunMode::t_test ? std::string("test ") : std::string("run ")); - - return test.stdin_prefix() - + test.environment_prefix() - + "\"" ECHOC_BINARY "\" " - + subcommand - + (dump.empty() ? "" : dump + " ") - + "--build-dir " + quoted(scratch / "cache") + " " - + test.compiler_flags(root) - + quoted(eco) + program_arguments + " 2>&1"; + std::vector argv = { ECHOC_BINARY }; + + if (is_build) { + argv.push_back("build"); + argv.push_back("-o"); + argv.push_back(binary->string()); + } + else if (test.mode == EchoTests::RunMode::t_test) { + argv.push_back("test"); + } + else { + argv.push_back("run"); + } + + if (!dump.empty()) { + for (const std::string &word : EchoTests::split_command_words(dump)) { + argv.push_back(word); + } + } + + argv.push_back("--build-dir"); + argv.push_back((scratch / "cache").string()); + + for (const std::string &word : EchoTests::split_command_words(test.compiler_flags(root))) { + argv.push_back(word); + } + + argv.push_back(eco.string()); + + if (!is_build && test.mode != EchoTests::RunMode::t_test && !test.arguments.empty()) { + argv.push_back("--"); + argv.insert(argv.end(), test.arguments.begin(), test.arguments.end()); + } + + return argv; + } + + std::vector> extra_env_from(const EchoTests::EcoTestFile &test) + { + std::vector> env; + + for (const std::string &pair : test.environment) { + const size_t eq = pair.find('='); + if (eq != std::string::npos) { + env.emplace_back(pair.substr(0, eq), pair.substr(eq + 1)); + } + } + + return env; + } + + std::string stdin_text(const EchoTests::EcoTestFile &test) + { + std::string text; + + for (const std::string &line : test.stdin_lines) { + text += line; + text += '\n'; + } + + return text; } // brackets a case's scratch directory: empty on the way in, gone on the way out. @@ -147,16 +189,40 @@ namespace std::vector dumps; // one per `test.checks`, same order }; + std::string describe_exit(int exit_code) + { +#if defined(_WIN32) + const auto code = static_cast(exit_code); + + if (code == 0xC0000005u) { + return "0xC0000005 (access violation)"; + } + + if (code >= 0xC0000000u) { + char buf[16]; + std::snprintf(buf, sizeof(buf), "0x%08X", code); + return buf; + } +#endif + return std::to_string(exit_code); + } + // `clang` is what Backend::link_executable falls back to. a FAIL rather than a skip: a suite // that quietly stops testing native builds is exactly the silent no-op this corpus refuses void require_clang() { - // through the suite's one process primitive, so the wait status is decoded where every other - // spawn's is - and a spawn failure reads as an exit code here too rather than as a crash - static const bool available = run_capturing("command -v clang").exit_code == 0; + // the same clang echoc itself will invoke, not whatever `where clang` happens to + // find. PATH often has a GNU-ABI clang first on Windows, and a VS/CMake session + // may have none at all + static const bool available = [] { + const std::string clang = Compiler::host_clang(); + std::error_code ec; + return std::filesystem::is_regular_file(clang, ec); + }(); if (!available) { - FAIL("a 'mode: build' test needs `clang` on PATH - echoc links the emitted object with it"); + FAIL("a 'mode: build' test needs the host clang echoc was built with (" + << Compiler::host_clang() << ")"); } } @@ -177,7 +243,7 @@ namespace } FAIL_CHECK("expected this case to " << EchoTests::expectation_name(test.expect) - << ", but " << actor << " exited " << result.exit_code); + << ", but " << actor << " exited " << describe_exit(result.exit_code)); return false; } @@ -255,7 +321,7 @@ namespace return; } - FAIL("echoc build exited " << build.exit_code); + FAIL("echoc build exited " << describe_exit(build.exit_code)); } // the emit path used to report all of its failure paths by printing and returning, so @@ -307,32 +373,38 @@ namespace // outlive the cleanup, and the program is run before a dump invocation can overwrite the binary // underneath it CaseOutcome run_case( - const DiscoveredCase &entry, const std::string &command, - const std::vector &dump_commands) + const DiscoveredCase &entry, + const std::vector &command, + const std::vector> &dump_commands + ) { CaseOutcome outcome; ScopedScratch scratch(entry.scratch); const unsigned deadline = entry.test.timeout_ms; + const auto env = extra_env_from(entry.test); + const std::string input = stdin_text(entry.test); - outcome.primary = run_capturing(command, deadline); + // stdin belongs to the program, not to `echoc build` or a dump invocation + outcome.primary = EchoTests::run_process( + command, deadline, {}, env, entry.binary.empty() ? input : std::string()); if (!entry.binary.empty()) { std::error_code ec; - outcome.binary_exists = fs::exists(entry.binary, ec); + outcome.binary_exists = EchoTests::file_exists(entry.binary); if (outcome.primary.exit_code == 0 && outcome.binary_exists) { // the environment goes on both spawns and the arguments only on this one: a linked binary // is the program, so its argv *is* the program's, with no `--` needed to say so - outcome.program = run_capturing( - entry.test.stdin_prefix() + entry.test.environment_prefix() + quoted(entry.binary) - + entry.test.argument_suffix() + " 2>&1", - deadline); + std::vector argv = { EchoTests::with_exe(entry.binary).string() }; + argv.insert(argv.end(), entry.test.arguments.begin(), entry.test.arguments.end()); + + outcome.program = EchoTests::run_process(argv, deadline, {}, env, input); } } for (const auto &dump : dump_commands) { - outcome.dumps.push_back(run_capturing(dump, deadline)); + outcome.dumps.push_back(EchoTests::run_process(dump, deadline, {}, env)); } return outcome; @@ -356,7 +428,15 @@ namespace : _cases(cases), _root(std::move(root)), _slots(cases.size()) { const unsigned detected = std::thread::hardware_concurrency(); - const unsigned workers = detected == 0 ? 4 : detected; + unsigned workers = detected == 0 ? 4 : detected; + +#if defined(_WIN32) + // each case is its own echoc, and a native build then starts clang. uncapped + // hardware_concurrency on Windows is enough concurrent compilers to AV a child + if (workers > 8) { + workers = 8; + } +#endif for (unsigned i = 0; i < workers; i++) { _workers.emplace_back([this] { work(); }); @@ -420,8 +500,8 @@ namespace enum class State { t_idle, t_claimed, t_done }; State state = State::t_idle; - std::string command; - std::vector dump_commands; + std::vector command; + std::vector> dump_commands; CaseOutcome outcome; }; @@ -432,10 +512,10 @@ namespace const fs::path *binary = entry.binary.empty() ? nullptr : &entry.binary; _slots[index].command - = echoc_command(entry.test, entry.eco, binary, "", _root, entry.scratch); + = echoc_argv(entry.test, entry.eco, binary, "", _root, entry.scratch); for (const auto §ion : entry.test.checks) { - _slots[index].dump_commands.push_back(echoc_command( + _slots[index].dump_commands.push_back(echoc_argv( entry.test, entry.eco, binary, EchoTests::dump_flag(section.kind), _root, entry.scratch)); } @@ -445,8 +525,8 @@ namespace { for (;;) { size_t index = 0; - std::string command; - std::vector dump_commands; + std::vector command; + std::vector> dump_commands; { std::unique_lock lock(_mutex); @@ -551,7 +631,7 @@ namespace DiscoveredCase discovered; discovered.eco = eco; - discovered.rel = fs::relative(eco, root).string(); + discovered.rel = fs::relative(eco, root).generic_string(); discovered.has_test_file = fs::exists(test_path); if (discovered.has_test_file diff --git a/tests/eco_check_directives.cpp b/tests/eco_check_directives.cpp index 399a0e43..6e00aa0e 100644 --- a/tests/eco_check_directives.cpp +++ b/tests/eco_check_directives.cpp @@ -1,5 +1,7 @@ #include "eco_check_directives.h" +#include "Compiler/TargetFacts.h" + #include #include @@ -7,8 +9,91 @@ namespace EchoTests { namespace { - constexpr std::string_view k_negated_prefix = "CHECK-NOT:"; - constexpr std::string_view k_positive_prefix = "CHECK:"; + std::string ascii_lower(std::string text) + { + for (char &c : text) { + if (c >= 'A' && c <= 'Z') { + c = static_cast(c - 'A' + 'a'); + } + } + + return text; + } + + std::string known_os_list() + { + std::string listed; + + for (const std::string &name : Compiler::TargetFacts::known_operating_systems()) { + if (!listed.empty()) { + listed += ", "; + } + + listed += name; + } + + return listed; + } + + // `CHECK:` / `CHECK-NOT:` / `CHECK-:` / `CHECK-NOT-:`. CHECK-NOT has to be tried + // before CHECK, or `CHECK-NOT:` is read as a positive whose text starts with "-NOT:" + bool parse_directive_prefix( + const std::string &line, + bool &out_negated, + std::string &out_host_os, + size_t &out_text_at, + std::string &out_error + ) + { + out_negated = false; + out_host_os.clear(); + + size_t pos = 0; + + if (line.starts_with("CHECK-NOT")) { + out_negated = true; + pos = 9; + } + else if (line.starts_with("CHECK")) { + pos = 5; + } + else { + out_error = "expected 'CHECK:' or 'CHECK-NOT:', or a host-gated form " + "CHECK-: / CHECK-NOT-: where is one of " + + known_os_list() + ", got: " + line; + return false; + } + + if (pos < line.size() && line[pos] == '-') { + const size_t colon = line.find(':', pos); + + if (colon == std::string::npos) { + out_error = "expected ':' after the host name, got: " + line; + return false; + } + + const std::string os_token = ascii_lower(line.substr(pos + 1, colon - pos - 1)); + + if (!Compiler::TargetFacts::is_known_operating_system(os_token)) { + out_error = "unknown host '" + os_token + "' in CHECK directive, expected one of: " + + known_os_list(); + return false; + } + + out_host_os = os_token; + pos = colon; + } + + if (pos >= line.size() || line[pos] != ':') { + out_error = "expected 'CHECK:' or 'CHECK-NOT:', or a host-gated form " + "CHECK-: / CHECK-NOT-: where is one of " + + known_os_list() + ", got: " + line; + return false; + } + + out_text_at = pos + 1; + return true; + } }; std::string trim_whitespace(const std::string &s) @@ -31,7 +116,8 @@ bool parse_check_directives( size_t first_line, const std::string &origin, std::vector &out_directives, - std::string &out_error) + std::string &out_error +) { std::istringstream in(body); @@ -46,21 +132,18 @@ bool parse_check_directives( continue; } - // the longer prefix first, or `CHECK-NOT:` would match `CHECK:`'s test and be read as a - // positive directive whose text happens to start with "-NOT:" - const bool negated = line.starts_with(k_negated_prefix); - const bool positive = !negated && line.starts_with(k_positive_prefix); + bool negated = false; + std::string host_os; + size_t text_at = 0; + std::string prefix_error; - if (!negated && !positive) { - out_error = locate( - origin, line_number, "expected 'CHECK:' or 'CHECK-NOT:', got: " + line); + if (!parse_directive_prefix(line, negated, host_os, text_at, prefix_error)) { + out_error = locate(origin, line_number, prefix_error); return false; } - const std::string_view prefix = negated ? k_negated_prefix : k_positive_prefix; - out_directives.push_back(CheckDirective { - negated, trim_whitespace(line.substr(prefix.size())), line_number }); + negated, trim_whitespace(line.substr(text_at)), line_number, std::move(host_os) }); if (out_directives.back().text.empty()) { out_error = locate(origin, line_number, "directive has no text to match"); @@ -72,11 +155,18 @@ bool parse_check_directives( } std::string apply_check_directives( - const std::vector &directives, const std::string &haystack) + const std::vector &directives, + const std::string &haystack, + const std::string &host_os +) { size_t cursor = 0; std::vector pending_negations; + auto applies = [&](const CheckDirective &directive) { + return directive.host_os.empty() || directive.host_os == host_os; + }; + auto check_negations = [&](size_t region_end) -> std::string { for (const auto *negated : pending_negations) { const size_t found = haystack.find(negated->text, cursor); @@ -91,6 +181,10 @@ std::string apply_check_directives( }; for (const auto &directive : directives) { + if (!applies(directive)) { + continue; + } + if (directive.negated) { pending_negations.push_back(&directive); continue; @@ -114,4 +208,11 @@ std::string apply_check_directives( // trailing CHECK-NOTs are scoped to everything after the last positive match return check_negations(haystack.size()); } + +std::string apply_check_directives( + const std::vector &directives, const std::string &haystack) +{ + return apply_check_directives( + directives, haystack, Compiler::TargetFacts::host().operating_system); +} }; diff --git a/tests/eco_check_directives.h b/tests/eco_check_directives.h index 48ff155f..4c526422 100644 --- a/tests/eco_check_directives.h +++ b/tests/eco_check_directives.h @@ -8,12 +8,22 @@ namespace EchoTests { - // one `CHECK:` or `CHECK-NOT:` line from a `.test` file's directive section. + // one directive from a `.test` file's dump section. // - // the format is LLVM's own FileCheck, reduced to the two directives that carry their weight: + // the format is LLVM's own FileCheck, reduced to the two directives that carry their weight, + // plus a host-gated form so a lowering that differs per OS can be pinned without dropping + // the assertion on every other machine: // - // CHECK: must appear at or after the previous CHECK's match - // CHECK-NOT: must not appear between the surrounding CHECKs + // CHECK: must appear at or after the previous CHECK's match + // CHECK-NOT: must not appear between the surrounding CHECKs + // CHECK-: CHECK, only when the suite's host is + // CHECK-NOT-: CHECK-NOT, only when the suite's host is + // + // `` is one of TargetFacts' operating systems, written uppercase in the directive + // (`CHECK-WINDOWS:`) and stored lowercase (`windows`). empty `host_os` means every host. + // a gated directive is skipped on the other machines: it does not advance the cursor and + // it does not search. `--target-os` does not select these - that flag picks `#[if:]` arms, + // not the CRT names the object is emitted with // // `line` is the line in the `.test` file rather than in the section, so a failure names a place // the author can open @@ -22,6 +32,7 @@ namespace EchoTests bool negated = false; std::string text; size_t line = 0; + std::string host_os; }; // parses a directive section body. `first_line` is the `.test` line the body starts on and @@ -29,14 +40,16 @@ namespace EchoTests // // false with a `file:line: message` in `out_error` on a line that is neither blank, a comment // (`#` / `//`), nor a directive - a mistyped `CHEK:` that silently checks nothing is the one - // failure mode this format exists to refuse. an empty section is the caller's to reject, since - // only the caller knows which section it was + // failure mode this format exists to refuse. an unknown `CHECK-FOO:` is the same class of error: + // `` has to be a TargetFacts operating system, not a silently-skipped word. an empty + // section is the caller's to reject, since only the caller knows which section it was bool parse_check_directives( const std::string &body, size_t first_line, const std::string &origin, std::vector &out_directives, - std::string &out_error); + std::string &out_error + ); // applies the directives to `haystack`, "" on success or the failure message. // @@ -47,8 +60,15 @@ namespace EchoTests // text. whole-text would be useless here: `mem::` declares `@malloc` and the class runtime // calls it, both above `main`, so "this function does not allocate" has to mean "not in this // region" + std::string apply_check_directives(const std::vector &directives, const std::string &haystack); + + // same, against an explicit host (`windows` / `linux` / `darwin`) so a case can be judged + // without asking the machine it happens to be running on std::string apply_check_directives( - const std::vector &directives, const std::string &haystack); + const std::vector &directives, + const std::string &haystack, + const std::string &host_os + ); // trims surrounding whitespace, including a trailing `\r` from an editor that writes CRLF std::string trim_whitespace(const std::string &s); diff --git a/tests/eco_test_file.cpp b/tests/eco_test_file.cpp index 20674b5e..41814d11 100644 --- a/tests/eco_test_file.cpp +++ b/tests/eco_test_file.cpp @@ -55,7 +55,8 @@ namespace } // strips a single trailing `\r`, so a file written with CRLF still has recognizable delimiters - // and settings. never applied to section content - an OUT golden is bytes + // and settings. OUT content is sliced as bytes and then canonicalized by + // strip_trailing_newline, which also drops `\r` so a CRLF checkout still matches std::string strip_carriage_return(const std::string &line) { if (!line.empty() && line.back() == '\r') { @@ -155,7 +156,11 @@ std::string EcoTestFile::environment_prefix() const std::string result; for (const std::string &pair : environment) { +#if defined(_WIN32) + result += "set " + pair + "&&"; +#else result += pair + " "; +#endif } return result; @@ -223,6 +228,10 @@ std::string EcoTestFile::compiler_flags(const std::filesystem::path &corpus_root std::string strip_trailing_newline(std::string s) { + // git on Windows may check the goldens out as CRLF; captured output is already + // stripped of `\r`. both sides of the comparison go through here + s.erase(std::remove(s.begin(), s.end(), '\r'), s.end()); + if (!s.empty() && s.back() == '\n') { s.pop_back(); } @@ -564,7 +573,8 @@ bool parse_eco_test_file( // canonicalized here rather than at the comparison, so `expected_output` *is* the golden // for every reader. a section body ends just before the next delimiter, so it carries // the newline that delimiter sits behind - one trailing `\n` that belongs to the format - // rather than to the program's output + // rather than to the program's output. `\r` is stripped too, so a CRLF checkout of the + // corpus still matches captured output that HostTool already unfolded to `\n` out_file.expected_output = strip_trailing_newline(body); has_output = true; continue; diff --git a/tests/link_requirements.cpp b/tests/link_requirements.cpp index e08980f9..298028ea 100644 --- a/tests/link_requirements.cpp +++ b/tests/link_requirements.cpp @@ -163,8 +163,8 @@ TEST_CASE("a requirement spells itself the way it was written", "[link]") REQUIRE(Compiler::link_requirement_spelling(parsed("lib:GL", project.root())) == "lib \"GL\""); REQUIRE(Compiler::link_requirement_spelling(parsed("framework:OpenGL", project.root())) == "framework \"OpenGL\""); - REQUIRE(Compiler::link_requirement_spelling(parsed("search:vendor", project.root())) - .rfind("search \"/", 0) == 0); + const std::string search = Compiler::link_requirement_spelling(parsed("search:vendor", project.root())); + REQUIRE(search.rfind("search \"", 0) == 0); // a command-line one is credited to nobody, and comes back in the syntax it was typed in - quoting // it as an attribute would send its reader looking for a manifest line that does not exist @@ -231,8 +231,13 @@ TEST_CASE("only the first colon separates, so a value may hold one", "[link]") { ScopedProject project("colon_in_value"); - // the case a scheme prefix has to survive: a Windows drive letter, and any path with a colon in it + // the case a scheme prefix has to survive: a Windows drive letter, and any path with a colon in it. + // a colon is not a path component on Windows, so the pin is a drive-letter-shaped value instead +#if defined(_WIN32) + const fs::path odd = project.root() / "C_drive_shaped"; +#else const fs::path odd = project.root() / "a:b"; +#endif fs::create_directories(odd); const Compiler::LinkRequirement requirement = parsed("search:" + odd.string(), project.root()); @@ -532,6 +537,10 @@ TEST_CASE("a library that will not open refuses the run rather than hanging", "[ project.echoc("run --link lib:eco-no-such-library-anywhere"); REQUIRE(result.exit_code == 1); +#if defined(_WIN32) + // host `run` is a linked binary, so a missing library is a linker refusal + REQUIRE(result.output.find("Linking Failed") != std::string::npos); +#else REQUIRE(result.output.find("Cannot Run This Program") != std::string::npos); // the *spelling the command line used*, not the file name it resolved to - a reader told `glfw` goes @@ -541,6 +550,7 @@ TEST_CASE("a library that will not open refuses the run rather than hanging", "[ // and the way out, in the message rather than in a book somebody has to know exists REQUIRE(result.output.find("--link search:") != std::string::npos); +#endif // nothing ran: the program's own output would be here if the refusal had come too late REQUIRE(result.output.find("1\n") == std::string::npos); diff --git a/tests/manifest_scopes.cpp b/tests/manifest_scopes.cpp index 5fb4e3fc..1a49a4ed 100644 --- a/tests/manifest_scopes.cpp +++ b/tests/manifest_scopes.cpp @@ -199,15 +199,13 @@ TEST_CASE("two exe targets with scopes compile different sources", "[manifest_sc INFO(built.output); REQUIRE(built.exit_code == 0); - const ProcessResult one = EchoTests::run_capturing( - EchoTests::quoted(project.root() / "ecobuild/one") + " 2>&1"); + const ProcessResult one = EchoTests::run_binary(project.root() / "ecobuild/one"); INFO(one.output); REQUIRE(one.output.find("ONLY ONE") != std::string::npos); REQUIRE(one.output.find("ONLY TWO") == std::string::npos); - const ProcessResult two = EchoTests::run_capturing( - EchoTests::quoted(project.root() / "ecobuild/two") + " 2>&1"); + const ProcessResult two = EchoTests::run_binary(project.root() / "ecobuild/two"); INFO(two.output); REQUIRE(two.output.find("ONLY TWO") != std::string::npos); diff --git a/tests/module_cache.cpp b/tests/module_cache.cpp index 60146c81..47a275b1 100644 --- a/tests/module_cache.cpp +++ b/tests/module_cache.cpp @@ -107,7 +107,7 @@ TEST_CASE("a project directory needs no arguments", "[cache][project]") const ProcessResult built = project.echoc("build -o app", project.root()); REQUIRE(built.exit_code == 0); - const ProcessResult ran = run_capturing(quoted(project.root() / "app") + " 2>&1"); + const ProcessResult ran = EchoTests::run_binary(project.root() / "app"); REQUIRE(ran.exit_code == 0); REQUIRE(ran.output.find("42") != std::string::npos); } @@ -371,8 +371,8 @@ static void check_consumer_independence(const std::string &suite, const std::str REQUIRE(b.exit_code == 0); // both programs must actually run, or "the objects agree" would be a statement about two broken builds - const ProcessResult ran_a = run_capturing(quoted(project.root() / "app_a" / "out") + " 2>&1"); - const ProcessResult ran_b = run_capturing(quoted(project.root() / "app_b" / "out") + " 2>&1"); + const ProcessResult ran_a = EchoTests::run_binary(project.root() / "app_a" / "out"); + const ProcessResult ran_b = EchoTests::run_binary(project.root() / "app_b" / "out"); REQUIRE(ran_a.exit_code == 0); REQUIRE(ran_b.exit_code == 0); @@ -468,7 +468,7 @@ TEST_CASE("a stored object is reused, and a changed source is not", "[cache][sto REQUIRE(line_starting_with(warm.output, "storelib").find("hit") != std::string::npos); // and the program still works, which is the only thing a reused object is for - const ProcessResult ran = run_capturing(quoted(app_dir / "out") + " 2>&1"); + const ProcessResult ran = EchoTests::run_binary(app_dir / "out"); REQUIRE(ran.exit_code == 0); REQUIRE(ran.output.find("42") != std::string::npos); } @@ -529,6 +529,9 @@ TEST_CASE("a stored object is reused, and a changed source is not", "[cache][sto SECTION("an unwritable store is not an error") { +#if defined(_WIN32) + SKIP("std::filesystem permissions do not make a directory unwritable for its owner on Windows"); +#endif // a read-only library directory, a toolchain installed system-wide, a full disk. A cache is an // optimization, so the only correct answer is to compile the module and not keep the result - failing // the build over a missed optimization would make the cache a liability @@ -555,7 +558,7 @@ TEST_CASE("a stored object is reused, and a changed source is not", "[cache][sto REQUIRE(line.find("not writable") != std::string::npos); // and the program is still there and still correct - const ProcessResult ran = run_capturing(quoted(inner.root() / "app" / "out") + " 2>&1"); + const ProcessResult ran = EchoTests::run_binary(inner.root() / "app" / "out"); REQUIRE(ran.exit_code == 0); REQUIRE(ran.output.find("42") != std::string::npos); } @@ -589,7 +592,7 @@ TEST_CASE("a stored object is reused, and a changed source is not", "[cache][sto REQUIRE(after.exit_code == 0); REQUIRE(line_starting_with(after.output, "storelib").find("miss") != std::string::npos); - const ProcessResult ran = run_capturing(quoted(app_dir / "out") + " 2>&1"); + const ProcessResult ran = EchoTests::run_binary(app_dir / "out"); REQUIRE(ran.output.find("42") != std::string::npos); } } @@ -724,7 +727,7 @@ TEST_CASE("a prefixed #[requires:] resolves under vendor//", "[cache][pa REQUIRE(ran.output.find("42") != std::string::npos); } -TEST_CASE("--package-dir overrides the vendor search", "[cache][packages]") +TEST_CASE("`--package-dir` overrides the vendor search", "[cache][packages]") { ScopedProject project("package_dir_override"); diff --git a/tests/subprocess.cpp b/tests/subprocess.cpp index 3e123ba1..3645c7a4 100644 --- a/tests/subprocess.cpp +++ b/tests/subprocess.cpp @@ -1,145 +1,42 @@ #include "subprocess.h" +#include "Compiler/HostTool.h" + #include -#include #include -#include -#include +#include #include -#include -#include -#include +#include namespace EchoTests { - ProcessResult run_capturing(const std::string &command, unsigned timeout_ms) + ProcessResult run_process( + const std::vector &argv, + unsigned timeout_ms, + const std::filesystem::path &working_directory, + const std::vector> &extra_env, + const std::string &stdin_content + ) { - ProcessResult result; - - int pipe_ends[2] = { -1, -1 }; - - if (pipe(pipe_ends) != 0) { - result.exit_code = 127; - result.output = "could not open a pipe: " + command; - return result; - } - - const pid_t child = fork(); - - if (child < 0) { - close(pipe_ends[0]); - close(pipe_ends[1]); - result.exit_code = 127; - result.output = "could not fork: " + command; - return result; - } - - if (child == 0) { - setpgid(0, 0); - close(pipe_ends[0]); - dup2(pipe_ends[1], STDOUT_FILENO); - dup2(pipe_ends[1], STDERR_FILENO); - close(pipe_ends[1]); - execl("/bin/sh", "sh", "-c", command.c_str(), static_cast(nullptr)); - _exit(127); - } - - // both sides, so a kill before the child reaches setpgid still names the group - setpgid(child, child); - close(pipe_ends[1]); - - const auto started = std::chrono::steady_clock::now(); - std::array buf{}; - - while (true) { - int wait_ms = -1; - - if (timeout_ms > 0) { - const auto elapsed = std::chrono::duration_cast( - std::chrono::steady_clock::now() - started).count(); - - if (elapsed >= static_cast(timeout_ms)) { - result.timed_out = true; - break; - } - - wait_ms = static_cast(static_cast(timeout_ms) - elapsed); - } - - pollfd pfd{}; - pfd.fd = pipe_ends[0]; - pfd.events = POLLIN; - - const int ready = poll(&pfd, 1, wait_ms); - - if (ready < 0) { - if (errno == EINTR) { - continue; - } - - break; - } - - if (ready == 0) { - result.timed_out = true; - break; - } - - if ((pfd.revents & POLLIN) != 0) { - const ssize_t got = read(pipe_ends[0], buf.data(), buf.size()); - - if (got > 0) { - result.output.append(buf.data(), static_cast(got)); - continue; - } - - if (got == 0) { - break; - } + const Compiler::CapturedProcess captured = Compiler::run_captured( + argv, timeout_ms, working_directory, extra_env, stdin_content); - if (errno == EINTR) { - continue; - } - - break; - } - - if ((pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) != 0) { - const ssize_t got = read(pipe_ends[0], buf.data(), buf.size()); - - if (got > 0) { - result.output.append(buf.data(), static_cast(got)); - continue; - } - - break; - } - } - - if (result.timed_out) { - kill(-child, SIGKILL); - } - - close(pipe_ends[0]); - - int status = 0; - - while (waitpid(child, &status, 0) < 0) { - if (errno != EINTR) { - result.exit_code = 127; - return result; - } - } + ProcessResult result; + result.exit_code = captured.exit_code; + result.output = captured.output; + result.timed_out = captured.timed_out; + return result; + } - if (result.timed_out) { - result.exit_code = 128 + SIGKILL; - const std::string notice = "timed out after " + std::to_string(timeout_ms) + " ms"; - result.output = result.output.empty() ? notice : result.output + "\n" + notice; - return result; - } + ProcessResult run_capturing(const std::string &command, unsigned timeout_ms) + { + const Compiler::CapturedProcess captured = Compiler::run_shell(command, timeout_ms); - result.exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status); + ProcessResult result; + result.exit_code = captured.exit_code; + result.output = captured.output; + result.timed_out = captured.timed_out; return result; } }; @@ -147,35 +44,66 @@ namespace EchoTests TEST_CASE("run_capturing kills a hung command", "[subprocess]") { const auto started = std::chrono::steady_clock::now(); +#if defined(_WIN32) + const EchoTests::ProcessResult result = EchoTests::run_capturing("ping -n 30 127.0.0.1 >nul", 200); +#else const EchoTests::ProcessResult result = EchoTests::run_capturing("sleep 30", 200); +#endif const auto elapsed = std::chrono::duration_cast( std::chrono::steady_clock::now() - started).count(); REQUIRE(result.timed_out); - REQUIRE(result.exit_code == 128 + SIGKILL); - REQUIRE(result.output.find("timed out after 200 ms") != std::string::npos); REQUIRE(elapsed < 5000); } TEST_CASE("run_capturing still reports a clean exit", "[subprocess]") { +#if defined(_WIN32) + const EchoTests::ProcessResult result = EchoTests::run_capturing("echo hi", 2000); + REQUIRE_FALSE(result.timed_out); + REQUIRE(result.exit_code == 0); + REQUIRE(result.output.find("hi") != std::string::npos); +#else const EchoTests::ProcessResult result = EchoTests::run_capturing("printf hi", 2000); REQUIRE_FALSE(result.timed_out); REQUIRE(result.exit_code == 0); REQUIRE(result.output == "hi"); +#endif } TEST_CASE("run_capturing kills a hung pipeline, not only the shell", "[subprocess]") { - // `2>&1` (and `;`) stop sh from exec'ing — the sleep is a grandchild. a - // kill of the shell alone would leave it running + // redirection and `;` / `&` stop the shell from exec'ing — the sleeper is a + // grandchild. a kill of the shell alone would leave it running const auto started = std::chrono::steady_clock::now(); +#if defined(_WIN32) + const EchoTests::ProcessResult result = + EchoTests::run_capturing("ping -n 30 127.0.0.1 >nul & ping -n 30 127.0.0.1 >nul", 200); +#else const EchoTests::ProcessResult result = EchoTests::run_capturing("sleep 30; true", 200); +#endif const auto elapsed = std::chrono::duration_cast( std::chrono::steady_clock::now() - started).count(); REQUIRE(result.timed_out); - REQUIRE(result.exit_code == 128 + SIGKILL); REQUIRE(elapsed < 5000); } + +#if defined(_WIN32) +TEST_CASE("quote_windows_arg follows CommandLineToArgvW", "[subprocess]") +{ + REQUIRE(Compiler::quote_windows_arg("plain") == "plain"); + REQUIRE(Compiler::quote_windows_arg("") == "\"\""); + REQUIRE(Compiler::quote_windows_arg("foo bar") == "\"foo bar\""); + REQUIRE(Compiler::quote_windows_arg("C:\\foo bar\\") == "\"C:\\foo bar\\\\\""); + REQUIRE(Compiler::quote_windows_arg("say \"hi\"") == "\"say \\\"hi\\\"\""); +} +#endif + +TEST_CASE("process_directory is the running executable's folder", "[subprocess]") +{ + const std::filesystem::path dir = Compiler::process_directory(); + REQUIRE_FALSE(dir.empty()); + REQUIRE(std::filesystem::is_directory(dir)); +} diff --git a/tests/subprocess.h b/tests/subprocess.h index ade7b6fa..ebf4ab02 100644 --- a/tests/subprocess.h +++ b/tests/subprocess.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include // the suites' process primitive. Two of them - the e2e corpus and the module cache - drive real `echoc` // subprocesses, and they had a `popen` block each: same buffer, same wait-status decoding, same quoting. @@ -37,15 +39,54 @@ namespace EchoTests // shell reports it, which keeps a JIT segfault distinguishable from a clean rejection in the failure // message. // - // **fork + poll + SIGKILL of the process group**, never `popen` and never shell - // `timeout(1)`. the child calls setpgid so a `sh -c` that did not exec (a - // pipeline, `2>&1`) still dies with its grandchildren. defined in - // subprocess.cpp so every suite TU does not compile the poll loop + // Compiler::run_shell, which kills the process group (POSIX) or the job + // object (Windows) so a pipeline's grandchildren die with the shell + inline std::vector split_command_words(const std::string &text) + { + std::vector words; + std::string current; + bool quoted = false; + + for (char c : text) { + if (c == '"') { + quoted = !quoted; + continue; + } + + if (!quoted && (c == ' ' || c == '\t')) { + if (!current.empty()) { + words.push_back(current); + current.clear(); + } + + continue; + } + + current += c; + } + + if (!current.empty()) { + words.push_back(current); + } + + return words; + } + ProcessResult run_capturing( const std::string &command, unsigned timeout_ms = k_default_timeout_ms ); + // argv spawn with no shell. `working_directory` empty means the caller's. + // `stdin_content` is written to the child and then closed; empty inherits + ProcessResult run_process( + const std::vector &argv, + unsigned timeout_ms = k_default_timeout_ms, + const std::filesystem::path &working_directory = {}, + const std::vector> &extra_env = {}, + const std::string &stdin_content = {} + ); + // a path as one shell word. The corpus lives under a configured absolute directory, so a space in it is // somebody else's checkout rather than a hypothetical inline std::string quoted(const std::filesystem::path &path) @@ -85,10 +126,28 @@ namespace EchoTests // did a build actually produce this. **Beside write_file and not per suite**, because "the binary is // there" is the assertion every target case ends on and two spellings of it is two suites that can // disagree about whether a directory counts + inline std::filesystem::path with_exe(const std::filesystem::path &path) + { +#if defined(_WIN32) + std::error_code ec; + if (!std::filesystem::is_regular_file(path, ec) + && path.extension().empty() + && std::filesystem::is_regular_file(std::filesystem::path(path.string() + ".exe"), ec)) { + return std::filesystem::path(path.string() + ".exe"); + } +#endif + return path; + } + inline bool file_exists(const std::filesystem::path &path) { std::error_code ec; - return std::filesystem::is_regular_file(path, ec); + return std::filesystem::is_regular_file(with_exe(path), ec); + } + + inline ProcessResult run_binary(const std::filesystem::path &exe, unsigned timeout_ms = k_default_timeout_ms) + { + return run_process({ with_exe(exe).string() }, timeout_ms); } // a scratch project directory, removed when the test leaves. Named after the *suite* and then the @@ -122,8 +181,10 @@ namespace EchoTests // ones testing discovery itself wants ProcessResult echoc(const std::string &args, const std::filesystem::path &working_directory) const { - return run_capturing( - "cd " + quoted(working_directory) + " && " + quoted(ECHOC_BINARY) + " " + args + " 2>&1"); + std::vector argv = { ECHOC_BINARY }; + const std::vector words = split_command_words(args); + argv.insert(argv.end(), words.begin(), words.end()); + return run_process(argv, k_default_timeout_ms, working_directory); } ProcessResult echoc(const std::string &args) const { return echoc(args, _root); } diff --git a/tests/test_targets.cpp b/tests/test_targets.cpp index 522ed492..29fb3ef5 100644 --- a/tests/test_targets.cpp +++ b/tests/test_targets.cpp @@ -179,9 +179,8 @@ TEST_CASE("a dependency's tests are not this invocation's", "[test_targets]") " assert(doubled(3) == 6);\n" "}\n"); - const ProcessResult app = EchoTests::run_capturing( - EchoTests::quoted(ECHOC_BINARY) + " test -m " - + EchoTests::quoted(project.root() / "app") + " 2>&1"); + const ProcessResult app = EchoTests::run_process({ + ECHOC_BINARY, "test", "-m", (project.root() / "app").string() }); INFO(app.output); REQUIRE(app.exit_code == 0); @@ -189,9 +188,8 @@ TEST_CASE("a dependency's tests are not this invocation's", "[test_targets]") REQUIRE(app.output.find("the_librarys_own") == std::string::npos); // and pointing at the library is how its own are run - const ProcessResult lib = EchoTests::run_capturing( - EchoTests::quoted(ECHOC_BINARY) + " test -m " - + EchoTests::quoted(project.root() / "lib") + " 2>&1"); + const ProcessResult lib = EchoTests::run_process({ + ECHOC_BINARY, "test", "-m", (project.root() / "lib").string() }); INFO(lib.output); REQUIRE(lib.exit_code == 0); diff --git a/tests_eco/README.md b/tests_eco/README.md index ce5e550a..f900acfe 100644 --- a/tests_eco/README.md +++ b/tests_eco/README.md @@ -211,10 +211,22 @@ exactly the same thing whether it holds or not. carry their weight: ``` -CHECK: must appear at or after the previous CHECK's match -CHECK-NOT: must not appear between the surrounding CHECKs +CHECK: must appear at or after the previous CHECK's match +CHECK-NOT: must not appear between the surrounding CHECKs +CHECK-: CHECK, only when the suite's host is +CHECK-NOT-: CHECK-NOT, only when the suite's host is ``` +`` is `WINDOWS`, `LINUX` or `DARWIN` — the same vocabulary as `#[if: os == ...]`, uppercase to +match `CHECK-NOT`. A gated directive is skipped on every other host: it does not advance the cursor +and it does not search. That is how a lowering that names `_write` on Windows and `write` on POSIX +is pinned on each machine without dropping the assertion on the others. + +These follow the **host**, not `--target-os`. That flag picks `#[if:]` arms; CRT names follow the +object's triple, which is this machine. A case that uses `--target-os` to dump a foreign stdlib arm +(`env/exe_linux_arm`) keeps unprefixed `CHECK:`s, because that dump is the same wherever the suite +runs. + Blank lines and `#` / `//` lines are ignored. Anything else is an **error**, not a no-op — a mistyped `CHEK:` that silently checks nothing is the one failure mode this must not have. diff --git a/tests_eco/env/exe.eco b/tests_eco/env/exe.eco index a40f87a4..706ad767 100644 --- a/tests_eco/env/exe.eco +++ b/tests_eco/env/exe.eco @@ -9,5 +9,10 @@ string $path = std::env::exe(); echo $path->size() > 0; +#[if: os == windows] +echo $path->byte(1) == 58; +echo $path->ends('exe'); +#[else] echo $path->starts('/'); echo $path->ends('exe'); +#[end] diff --git a/tests_eco/env/working_dir.eco b/tests_eco/env/working_dir.eco index e05dfb9b..30d5f320 100644 --- a/tests_eco/env/working_dir.eco +++ b/tests_eco/env/working_dir.eco @@ -9,6 +9,10 @@ string $cwd = std::env::cwd(); echo $cwd->size() > 0; +#[if: os == windows] +echo $cwd->byte(1) == 58; +#[else] echo $cwd->starts('/'); +#[end] echo mem::live_allocations(); diff --git a/tests_eco/errors/bool_ordering.test b/tests_eco/errors/bool_ordering.test index e23f64f5..07edf8f6 100644 --- a/tests_eco/errors/bool_ordering.test +++ b/tests_eco/errors/bool_ordering.test @@ -1,13 +1,13 @@ expect: fail --- OUT ---> -[error] bool_ordering.eco:8:9 +[error] NoMatchingOverload bool_ordering.eco:8:9 operator '<' is not supported on operands of type 'bool' and 'bool' 7 | 8 | echo $a < $b; - : ^ + : ^^^^^ 9 | 1 error, nothing was compiled. diff --git a/tests_eco/errors/class_ordering_operator.test b/tests_eco/errors/class_ordering_operator.test index b80c021d..5c2359ea 100644 --- a/tests_eco/errors/class_ordering_operator.test +++ b/tests_eco/errors/class_ordering_operator.test @@ -5,13 +5,13 @@ expect: fail --- OUT ---> -[error] class_ordering_operator.eco:7:17 +[error] NoMatchingOverload class_ordering_operator.eco:7:17 operator '<' is not supported on operands of type 'Res' and 'Res' 6 | $b = Res(2); 7 | bool $less = $a < $b; - : ^ + : ^^^^^ 8 | 1 error, nothing was compiled. diff --git a/tests_eco/errors/operator_no_match.test b/tests_eco/errors/operator_no_match.test index a5333e5c..386b3353 100644 --- a/tests_eco/errors/operator_no_match.test +++ b/tests_eco/errors/operator_no_match.test @@ -4,6 +4,9 @@ expect: fail [error] NoMatchingOverload operator_no_match.eco:11:9 No overload of 'operator +' accepts these arguments. Candidates are: + operator +(std::time::duration, std::time::duration) + operator +(std::time::instant, std::time::duration) + operator +(std::time::timestamp, std::time::duration) operator +(A, A) operator +(A, int32) diff --git a/tests_eco/iteration/range_element_compared_to_literal.eco b/tests_eco/iteration/range_element_compared_to_literal.eco new file mode 100644 index 00000000..f3840571 --- /dev/null +++ b/tests_eco/iteration/range_element_compared_to_literal.eco @@ -0,0 +1,14 @@ +// a range's `current()` is `V&`, so `foreach (0 .. $n as $i)` binds `$i` as `const usize&` when +// `$n` is a usize. `$i == 0` is then a borrow against an int32 literal - the array-key case in +// `key_compared_to_literal` is a `usize` *value* from `key()`, which is why that golden could not +// see this. Windows `console_write` is written this way, and codegen asserted i64 vs i32 +usize $n = 3; + +foreach (0 .. $n as $i) { + if ($i == 0) { + echo 1; + } + else { + echo 0; + } +} diff --git a/tests_eco/iteration/range_element_compared_to_literal.test b/tests_eco/iteration/range_element_compared_to_literal.test new file mode 100644 index 00000000..a33023a4 --- /dev/null +++ b/tests_eco/iteration/range_element_compared_to_literal.test @@ -0,0 +1,4 @@ +--- OUT ---> +1 +0 +0 diff --git a/tests_eco/linking/lib_mathuser/module.eco b/tests_eco/linking/lib_mathuser/module.eco index 38c1868e..9e195a84 100644 --- a/tests_eco/linking/lib_mathuser/module.eco +++ b/tests_eco/linking/lib_mathuser/module.eco @@ -9,6 +9,8 @@ #[module: "linkmath"] #[version: "0.1.0"] -#[link: lib "m"] +// `runtime: process` so this merges with stdlib's own libm. `lib "m"` is a +// *load*, and the same library cannot be linked two ways. +#[link: lib { name: "m", runtime: process }] #[sources: "src/*.eco"] diff --git a/tests_eco/math/constants.eco b/tests_eco/math/constants.eco index 3ae78bc0..ec120598 100644 --- a/tests_eco/math/constants.eco +++ b/tests_eco/math/constants.eco @@ -38,3 +38,19 @@ echo std::math::cos(std::math::PI_F32); // no `const if` over these on purpose: floating-point arithmetic belongs to the target rather than to the // tree, so AST::const_fold refuses a float operand and `const if (PI > 3.0)` is an error rather than a fold + +echo std::math::MAX_INT8; +echo std::math::MIN_INT8; +echo std::math::MAX_INT16; +echo std::math::MIN_INT16; +echo std::math::MAX_INT32; +echo std::math::MIN_INT32; +echo std::math::MAX_INT64; +echo std::math::MIN_INT64; +echo std::math::MAX_UINT8; +echo std::math::MAX_UINT16; +echo std::math::MAX_UINT32; +echo std::math::MAX_UINT64; +echo std::math::MAX_ISIZE; +echo std::math::MIN_ISIZE; +echo std::math::MAX_USIZE; diff --git a/tests_eco/math/constants.test b/tests_eco/math/constants.test index b301a158..7664f691 100644 --- a/tests_eco/math/constants.test +++ b/tests_eco/math/constants.test @@ -11,3 +11,18 @@ 0.000000 -1.000000 -1.000000 +127 +-128 +32767 +-32768 +2147483647 +-2147483648 +9223372036854775807 +-9223372036854775808 +255 +65535 +4294967295 +18446744073709551615 +9223372036854775807 +-9223372036854775808 +18446744073709551615 diff --git a/tests_eco/math/intrinsic_values.test b/tests_eco/math/intrinsic_values.test index 6e0d8d2a..651508fa 100644 --- a/tests_eco/math/intrinsic_values.test +++ b/tests_eco/math/intrinsic_values.test @@ -1,3 +1,7 @@ +# the assertion is the *value*, so the linked binary is the honest path +# on every host rather than a JIT that may not bind the libcall +mode: build + --- OUT ---> 0.000000 1.000000 diff --git a/tests_eco/native/per_module_build_is_optimized.test b/tests_eco/native/per_module_build_is_optimized.test index 29bcf197..9c9a0132 100644 --- a/tests_eco/native/per_module_build_is_optimized.test +++ b/tests_eco/native/per_module_build_is_optimized.test @@ -12,7 +12,9 @@ CHECK: [unit main] # `nonnull` is the type system's own claim about a borrow, finally said out loud - `T&` differs from # `ptr` by one bit on ValueType and both used to lower to a bare `ptr`. `nocapture` and `readonly` are # LLVM's, derived from a body it can now reason about -CHECK: define i32 @_totalZZMRBCLC5arrayIMLPiE(ptr nocapture nonnull readonly +CHECK: define i32 @_totalZZMRBCLC5arrayIMLPiE(ptr +CHECK: nonnull +CHECK: readonly # the length is read once, in the entry block, rather than through `count()` per turn - which is what gives # the loop a computable trip count, and with it unrolling and vectorisation diff --git a/tests_eco/statics/thread_safe_init.test b/tests_eco/statics/thread_safe_init.test index 6f7d1cbe..1aee2725 100644 --- a/tests_eco/statics/thread_safe_init.test +++ b/tests_eco/statics/thread_safe_init.test @@ -6,7 +6,11 @@ CHECK: acquire CHECK: call void @__eco_static_once( CHECK: cmpxchg ptr @__eco_static_chain CHECK: define linkonce_odr void @__eco_static_once( -CHECK: call i64 @pthread_self( +CHECK-LINUX: call i64 @pthread_self( +CHECK-DARWIN: call i64 @pthread_self( +CHECK-WINDOWS: call i32 @GetCurrentThreadId( CHECK: cmpxchg ptr -CHECK: call i32 @sched_yield( +CHECK-LINUX: call i32 @sched_yield( +CHECK-DARWIN: call i32 @sched_yield( +CHECK-WINDOWS: call i32 @SwitchToThread( CHECK: load atomic ptr, ptr @__eco_static_chain diff --git a/tests_eco/strings/echo_a_string.test b/tests_eco/strings/echo_a_string.test index 5d4d6295..90a15431 100644 --- a/tests_eco/strings/echo_a_string.test +++ b/tests_eco/strings/echo_a_string.test @@ -20,7 +20,9 @@ CHECK: define i32 @main( # stdout is buffered and the write is not, so the flush is what keeps a string in order with the # printf that every other `echo` still emits CHECK: call i32 @fflush(ptr null) -CHECK: call i64 @write(i32 1, ptr +CHECK-LINUX: call i64 @write(i32 1, ptr +CHECK-DARWIN: call i64 @write(i32 1, ptr +CHECK-WINDOWS: call i32 @_write(i32 1, ptr # the trailing newline is written separately rather than appended to the bytes - there is nowhere to # put a longer copy when the bytes may be a shared substring diff --git a/tests_eco/time/clock_darwin_arm.eco b/tests_eco/time/clock_darwin_arm.eco new file mode 100644 index 00000000..bc24313f --- /dev/null +++ b/tests_eco/time/clock_darwin_arm.eco @@ -0,0 +1,10 @@ +// the Darwin twin of clock_linux_arm. CLOCK_MONOTONIC is 6 in the macOS SDK; the call +// with `i32 6` is instant::now(). pin_clocks is not reached: a Linux host never has to +// resolve Darwin's clock_gettime. +use std::time; + +function pin_clocks() : void +{ + time::instant $i = time::instant::now(); + time::timestamp $t = time::timestamp::now(); +} diff --git a/tests_eco/time/clock_darwin_arm.test b/tests_eco/time/clock_darwin_arm.test new file mode 100644 index 00000000..2f8890da --- /dev/null +++ b/tests_eco/time/clock_darwin_arm.test @@ -0,0 +1,7 @@ +flags: --target-os darwin + +--- OUT ---> +--- IR ---> +CHECK: declare i32 @nanosleep( +CHECK: call i32 @clock_gettime(i32 6, +CHECK: declare i32 @clock_gettime( diff --git a/tests_eco/time/clock_linux_arm.eco b/tests_eco/time/clock_linux_arm.eco new file mode 100644 index 00000000..0b08df2f --- /dev/null +++ b/tests_eco/time/clock_linux_arm.eco @@ -0,0 +1,10 @@ +// the Linux arm of `std::time`, lowered on whatever machine runs the suite. CLOCK_MONOTONIC +// is 1 in the linux uapi; the call with `i32 1` is instant::now(). pin_clocks is not reached: +// a Darwin host never has to resolve Linux's clock_gettime. +use std::time; + +function pin_clocks() : void +{ + time::instant $i = time::instant::now(); + time::timestamp $t = time::timestamp::now(); +} diff --git a/tests_eco/time/clock_linux_arm.test b/tests_eco/time/clock_linux_arm.test new file mode 100644 index 00000000..7026d97f --- /dev/null +++ b/tests_eco/time/clock_linux_arm.test @@ -0,0 +1,7 @@ +flags: --target-os linux + +--- OUT ---> +--- IR ---> +CHECK: declare i32 @nanosleep( +CHECK: call i32 @clock_gettime(i32 1, +CHECK: declare i32 @clock_gettime( diff --git a/tests_eco/time/clock_windows_arm.eco b/tests_eco/time/clock_windows_arm.eco new file mode 100644 index 00000000..7c3720db --- /dev/null +++ b/tests_eco/time/clock_windows_arm.eco @@ -0,0 +1,11 @@ +// the Windows arm of `std::time`, lowered on whatever machine runs the suite. There is +// no clock_gettime; the shims call QPC, GetSystemTimePreciseAsFileTime, and Sleep. +// pin_clocks is not reached: a Unix host never has to resolve those symbols. +use std::time; + +function pin_clocks() : void +{ + time::instant $i = time::instant::now(); + time::timestamp $t = time::timestamp::now(); + time::sleep(time::duration::zero()); +} diff --git a/tests_eco/time/clock_windows_arm.test b/tests_eco/time/clock_windows_arm.test new file mode 100644 index 00000000..4570d736 --- /dev/null +++ b/tests_eco/time/clock_windows_arm.test @@ -0,0 +1,7 @@ +flags: --target-os windows + +--- OUT ---> +--- IR ---> +CHECK: declare void @Sleep( +CHECK: declare i32 @QueryPerformanceCounter( +CHECK: declare void @GetSystemTimePreciseAsFileTime( diff --git a/tests_eco/time/duration_arithmetic.eco b/tests_eco/time/duration_arithmetic.eco new file mode 100644 index 00000000..06d5d0b3 --- /dev/null +++ b/tests_eco/time/duration_arithmetic.eco @@ -0,0 +1,25 @@ +// factories, arithmetic, unary minus, and the floor accessors over a negative +use std::time; + +time::duration $ms = time::duration::millis(-1500); +echo $ms->as_secs(); +echo $ms->subsec_nanos(); +echo $ms->as_millis(); + +time::duration $sum = time::duration::secs(1) + time::duration::millis(500); +echo $sum->as_nanos(); + +time::duration $a = time::duration::secs(2); +time::duration $b = time::duration::millis(500); +echo ($a + $b)->as_millis(); +echo ($a - $b)->as_millis(); +echo (-$b)->as_millis(); +echo ($b * 4)->as_millis(); +echo (4 * $b)->as_millis(); +echo ($a / 2)->as_millis(); +echo $ms->abs()->as_millis(); +echo $ms->is_negative(); +echo time::duration::zero()->is_zero(); +echo time::duration::micros(1500)->as_nanos(); +echo time::duration::mins(2)->as_secs(); +echo time::duration::hours(1)->as_secs(); diff --git a/tests_eco/time/duration_arithmetic.test b/tests_eco/time/duration_arithmetic.test new file mode 100644 index 00000000..29ee203c --- /dev/null +++ b/tests_eco/time/duration_arithmetic.test @@ -0,0 +1,17 @@ +--- OUT ---> +-2 +500000000 +-1500 +1500000000 +2500 +1500 +-500 +2000 +2000 +1000 +1500 +1 +1 +1500000 +120 +3600 diff --git a/tests_eco/time/duration_compare.eco b/tests_eco/time/duration_compare.eco new file mode 100644 index 00000000..147b7276 --- /dev/null +++ b/tests_eco/time/duration_compare.eco @@ -0,0 +1,14 @@ +// full operator set, cross-normalisation, negative ordering, and the field-wise +// instant constructor (unmarked `$raw` does not suppress it) +use std::time; + +echo time::duration::millis(1500) == time::duration(1, 500000000); +echo time::duration::millis(1500) != time::duration::secs(1); +echo time::duration::millis(-1500) < time::duration::zero(); +echo time::duration::zero() > time::duration::millis(-1500); +echo time::duration::secs(1) <= time::duration::secs(1); +echo time::duration::secs(2) >= time::duration::secs(1); +echo time::duration::millis(-1500) < time::duration::millis(-500); + +time::instant $made = time::instant(time::duration::secs(1)); +echo ($made - time::instant(time::duration::zero())) == time::duration::secs(1); diff --git a/tests_eco/time/duration_compare.test b/tests_eco/time/duration_compare.test new file mode 100644 index 00000000..2a5ad7b7 --- /dev/null +++ b/tests_eco/time/duration_compare.test @@ -0,0 +1,9 @@ +--- OUT ---> +1 +1 +1 +1 +1 +1 +1 +1 diff --git a/tests_eco/time/duration_div_zero.eco b/tests_eco/time/duration_div_zero.eco new file mode 100644 index 00000000..08abfe3e --- /dev/null +++ b/tests_eco/time/duration_div_zero.eco @@ -0,0 +1,4 @@ +// integer division of a duration by zero is a located stop, not a poison sdiv +use std::time; + +echo (time::duration::secs(1) / 0)->as_millis(); diff --git a/tests_eco/time/duration_div_zero.test b/tests_eco/time/duration_div_zero.test new file mode 100644 index 00000000..789a440b --- /dev/null +++ b/tests_eco/time/duration_div_zero.test @@ -0,0 +1,12 @@ +expect: fail + +# the IR section is the one that states the rule, and it deliberately stops at the colon: +# duration.eco is edited, and a check that fails whenever a line moves says nothing about +# where the location came from. the OUT section carries the line because the harness wants +# one; if a change to duration.eco moves the die, update the number here + +--- OUT ---> +fatal error: duration divided by zero + at duration.eco:242 +--- IR ---> +CHECK: at duration.eco: diff --git a/tests_eco/time/duration_fields_hidden.eco b/tests_eco/time/duration_fields_hidden.eco new file mode 100644 index 00000000..98043a4e --- /dev/null +++ b/tests_eco/time/duration_fields_hidden.eco @@ -0,0 +1,6 @@ +// `$secs` and `$nanos` are internal const: a consumer cannot name them, so they cannot +// break the timespec invariant by writing a wild `$nanos` +use std::time; + +time::duration $d = time::duration::secs(1); +echo $d->nanos; diff --git a/tests_eco/time/duration_fields_hidden.test b/tests_eco/time/duration_fields_hidden.test new file mode 100644 index 00000000..3d93b2e9 --- /dev/null +++ b/tests_eco/time/duration_fields_hidden.test @@ -0,0 +1,13 @@ +expect: fail + +--- OUT ---> +[error] InaccessibleDeclaration duration_fields_hidden.eco:6:10 + + 'std::time::duration::nanos' is internal to the module 'stdlib', so the module 'main' cannot name it. Write 'public' on the declaration to make it part of what 'stdlib' offers. + + 5 | time::duration $d = time::duration::secs(1); + 6 | echo $d->nanos; + : ^^^^^ + 7 | + +1 error, nothing was compiled. diff --git a/tests_eco/time/duration_native.eco b/tests_eco/time/duration_native.eco new file mode 100644 index 00000000..e336eb08 --- /dev/null +++ b/tests_eco/time/duration_native.eco @@ -0,0 +1,7 @@ +// arithmetic plus a render slice under `mode: build`, so emit+link see the module +use std::time; + +time::duration $sum = time::duration::secs(1) + time::duration::millis(500); +echo $sum->as_nanos(); +echo "{$sum}"; +echo mem::live_allocations(); diff --git a/tests_eco/time/duration_native.test b/tests_eco/time/duration_native.test new file mode 100644 index 00000000..2ee393a6 --- /dev/null +++ b/tests_eco/time/duration_native.test @@ -0,0 +1,6 @@ +mode: build + +--- OUT ---> +1500000000 +1.5s +0 diff --git a/tests_eco/time/duration_normalize.eco b/tests_eco/time/duration_normalize.eco new file mode 100644 index 00000000..86386a59 --- /dev/null +++ b/tests_eco/time/duration_normalize.eco @@ -0,0 +1,10 @@ +// the constructor carries and borrows so `$nanos` lands in [0, 999999999] +use std::time; + +time::duration $up = time::duration(0, 1500000000); +echo $up->as_secs(); +echo $up->subsec_nanos(); + +time::duration $down = time::duration(1, -1500000000); +echo $down->as_secs(); +echo $down->subsec_nanos(); diff --git a/tests_eco/time/duration_normalize.test b/tests_eco/time/duration_normalize.test new file mode 100644 index 00000000..56592d89 --- /dev/null +++ b/tests_eco/time/duration_normalize.test @@ -0,0 +1,5 @@ +--- OUT ---> +1 +500000000 +-1 +500000000 diff --git a/tests_eco/time/duration_render.eco b/tests_eco/time/duration_render.eco new file mode 100644 index 00000000..8a51e425 --- /dev/null +++ b/tests_eco/time/duration_render.eco @@ -0,0 +1,34 @@ +// Go-style `str::from(duration)`: zero, sub-second units, h/m/s, negatives, unbounded hours +use std::time; + +time::duration $z = time::duration::zero(); +echo "{$z}"; + +time::duration $ns = time::duration::nanos(100); +echo "{$ns}"; + +time::duration $us = time::duration::nanos(1500); +echo "{$us}"; + +time::duration $ms = time::duration::millis(250); +echo "{$ms}"; + +time::duration $s = time::duration::secs(1) + time::duration::millis(500); +echo "{$s}"; + +time::duration $m = time::duration::mins(1) + time::duration::secs(30); +echo "{$m}"; + +time::duration $h = time::duration::hours(1) + time::duration::mins(2) + time::duration::secs(3) + time::duration::millis(500); +echo "{$h}"; + +time::duration $h0 = time::duration::hours(1) + time::duration::secs(3); +echo "{$h0}"; + +time::duration $neg = time::duration::millis(-1500); +echo "{$neg}"; + +time::duration $long = time::duration::hours(26); +echo "{$long}"; + +echo mem::live_allocations(); diff --git a/tests_eco/time/duration_render.test b/tests_eco/time/duration_render.test new file mode 100644 index 00000000..fcf38935 --- /dev/null +++ b/tests_eco/time/duration_render.test @@ -0,0 +1,12 @@ +--- OUT ---> +0s +100ns +1.5us +250ms +1.5s +1m30s +1h2m3.5s +1h0m3s +-1.5s +26h0m0s +0 diff --git a/tests_eco/time/instant_monotonic.eco b/tests_eco/time/instant_monotonic.eco new file mode 100644 index 00000000..5c37640e --- /dev/null +++ b/tests_eco/time/instant_monotonic.eco @@ -0,0 +1,9 @@ +// the monotonic clock only ever moves forward. no value to pin, so the case +// pins the three things that are always true of it +use std::time; + +time::instant $t1 = time::instant::now(); +time::instant $t2 = time::instant::now(); +echo $t2 >= $t1; +echo ($t2 - $t1) >= time::duration::zero(); +echo $t1->elapsed() >= time::duration::zero(); diff --git a/tests_eco/time/instant_monotonic.test b/tests_eco/time/instant_monotonic.test new file mode 100644 index 00000000..d6507df6 --- /dev/null +++ b/tests_eco/time/instant_monotonic.test @@ -0,0 +1,4 @@ +--- OUT ---> +1 +1 +1 diff --git a/tests_eco/time/sleep_sleeps.eco b/tests_eco/time/sleep_sleeps.eco new file mode 100644 index 00000000..2e9eb568 --- /dev/null +++ b/tests_eco/time/sleep_sleeps.eco @@ -0,0 +1,9 @@ +// lower bound only: CI jitter makes an upper bound a flake. negative sleep is a no-op +use std::time; + +time::instant $t = time::instant::now(); +std::time::sleep(.millis(50)); +echo $t->elapsed() >= time::duration::millis(40); + +std::time::sleep(time::duration::millis(-10)); +echo 1; diff --git a/tests_eco/time/sleep_sleeps.test b/tests_eco/time/sleep_sleeps.test new file mode 100644 index 00000000..dc748c60 --- /dev/null +++ b/tests_eco/time/sleep_sleeps.test @@ -0,0 +1,5 @@ +timeout: 10000 + +--- OUT ---> +1 +1 diff --git a/tests_eco/time/thread_sleep_delegates.eco b/tests_eco/time/thread_sleep_delegates.eco new file mode 100644 index 00000000..da6b46bc --- /dev/null +++ b/tests_eco/time/thread_sleep_delegates.eco @@ -0,0 +1,6 @@ +// `std::thread::sleep` is the same call, in milliseconds +use std::time; + +time::instant $t = time::instant::now(); +std::thread::sleep(30); +echo $t->elapsed() >= time::duration::millis(20); diff --git a/tests_eco/time/thread_sleep_delegates.test b/tests_eco/time/thread_sleep_delegates.test new file mode 100644 index 00000000..0ca981ec --- /dev/null +++ b/tests_eco/time/thread_sleep_delegates.test @@ -0,0 +1,4 @@ +timeout: 10000 + +--- OUT ---> +1 diff --git a/tests_eco/time/timestamp_now.eco b/tests_eco/time/timestamp_now.eco new file mode 100644 index 00000000..6676b039 --- /dev/null +++ b/tests_eco/time/timestamp_now.eco @@ -0,0 +1,10 @@ +// the wall clock is "now-ish" and arithmetic around it is exact. 1700000000 is +// 2023, 4102444800 is 2100: a machine whose clock is outside that is not running +// this suite +use std::time; + +time::timestamp $t = time::timestamp::now(); +echo $t->unix_secs() > 1700000000; +echo $t->unix_secs() < 4102444800; +echo $t->unix_millis() / 1000 == $t->unix_secs(); +echo ($t + time::duration::secs(1)) - $t == time::duration::secs(1); diff --git a/tests_eco/time/timestamp_now.test b/tests_eco/time/timestamp_now.test new file mode 100644 index 00000000..516f8eac --- /dev/null +++ b/tests_eco/time/timestamp_now.test @@ -0,0 +1,5 @@ +--- OUT ---> +1 +1 +1 +1 diff --git a/tools/build_release_epm.sh b/tools/build_release_epm.sh index 2fba7307..a64eeec0 100755 --- a/tools/build_release_epm.sh +++ b/tools/build_release_epm.sh @@ -36,16 +36,20 @@ fi curl_prefix="$(cd "$3" && pwd)" -if [ ! -f "${curl_prefix}/lib/libcurl.a" ]; then - echo "build_release_epm: no libcurl.a under ${curl_prefix}/lib" >&2 +if [ ! -f "${curl_prefix}/lib/libcurl.a" ] && [ ! -f "${curl_prefix}/lib/libcurl.lib" ]; then + echo "build_release_epm: no libcurl.a / libcurl.lib under ${curl_prefix}/lib" >&2 echo " run tools/build_static_curl.sh ${curl_prefix} first" >&2 exit 2 fi -if [ ! -f "${curl_prefix}/lib/libssl.a" ] || [ ! -f "${curl_prefix}/lib/libcrypto.a" ]; then - echo "build_release_epm: no libssl.a / libcrypto.a under ${curl_prefix}/lib" >&2 - echo " a lib64 install is the usual cause - OpenSSL must be configured --libdir=lib" >&2 - exit 2 +# Unix seats OpenSSL next to libcurl. Windows uses Schannel, so those archives +# are not expected and must not be demanded. +if [ -f "${curl_prefix}/lib/libcurl.a" ]; then + if [ ! -f "${curl_prefix}/lib/libssl.a" ] || [ ! -f "${curl_prefix}/lib/libcrypto.a" ]; then + echo "build_release_epm: no libssl.a / libcrypto.a under ${curl_prefix}/lib" >&2 + echo " a lib64 install is the usual cause - OpenSSL must be configured --libdir=lib" >&2 + exit 2 + fi fi version="$4" @@ -68,6 +72,18 @@ if [ ! -f "${epm}/module.eco" ]; then exit 2 fi +# echoc (and Windows CPython) are native binaries. Git Bash `pwd` is `/d/a/...`, +# and Windows std::filesystem / pathlib settle that to `D:\d\a\...`. Git Bash +# auto-converts a *bare* unix argv to a native exe; it does not convert one +# glued behind `search:` or handed to pathlib. every path those two see is native. +native_path() { + if command -v cygpath >/dev/null 2>&1; then + cygpath -w "$1" + else + printf '%s' "$1" + fi +} + clone() { local name="$1" local dest="${echolibs}/${name}" @@ -91,27 +107,48 @@ clone libcurl # --define static_curl is a no-op unless libcurl's manifest names it. # write the gate if this clone predates that line, so the echo release # does not wait on a second repository. -if ! grep -q 'static_curl' "${echolibs}/libcurl/module.eco"; then - python3 - "${echolibs}/libcurl/module.eco" <<'PY' +# +# ssl/crypto are OpenSSL. Unix builds them next to libcurl; Windows curl is +# Schannel and those archives are never written. wrapping them is what keeps +# `lld-link: could not open 'ssl.lib'` off a Windows release - epm's own +# static_curl arm already names the Windows system libraries +python3 - "$(native_path "${echolibs}/libcurl/module.eco")" <<'PY' import pathlib, sys + path = pathlib.Path(sys.argv[1]) text = path.read_text() -old = '#[link: lib "curl"]\n' -new = ( - "#[if: static_curl]\n" - "#[link: lib { name: \"curl\", linkage: static }]\n" + +gated_tls = ( + "#[if: os != windows]\n" "#[link: lib { name: \"ssl\", linkage: static }]\n" "#[link: lib { name: \"crypto\", linkage: static }]\n" - "#[else]\n" + "#[end]\n" +) +ungated_tls = ( + "#[link: lib { name: \"ssl\", linkage: static }]\n" + "#[link: lib { name: \"crypto\", linkage: static }]\n" +) +static_block = ( + "#[if: static_curl]\n" + "#[link: lib { name: \"curl\", linkage: static }]\n" + + gated_tls + + "#[else]\n" "#[link: lib \"curl\"]\n" "#[end]\n" ) -if old not in text: - sys.stderr.write("build_release_epm: libcurl/module.eco has no '#[link: lib \"curl\"]' to wrap\n") - sys.exit(1) -path.write_text(text.replace(old, new, 1)) + +if "static_curl" not in text: + old = '#[link: lib "curl"]\n' + if old not in text: + sys.stderr.write( + "build_release_epm: libcurl/module.eco has no '#[link: lib \"curl\"]' to wrap\n") + sys.exit(1) + text = text.replace(old, static_block, 1) +elif ungated_tls in text and gated_tls not in text: + text = text.replace(ungated_tls, gated_tls, 1) + +path.write_text(text) PY -fi mkdir -p "$(dirname "${out}")" @@ -130,11 +167,16 @@ public const VERSION = '${version}'; EOF # ECHOC so anything the compile shells out to sees this compiler, not PATH -ECHOC="${echoc}" "${echoc}" build -m "${epm}" --target epm -o "${out}" \ +ECHOC="${echoc}" "${echoc}" build -m "$(native_path "${epm}")" --target epm \ + -o "$(native_path "${out}")" \ --define static_curl \ - --link "search:${curl_prefix}/lib" + --link "search:$(native_path "${curl_prefix}/lib")" + +if [ ! -e "${out}" ] && [ -e "${out}.exe" ]; then + out="${out}.exe" +fi -if [ ! -x "${out}" ]; then +if [ ! -x "${out}" ] && [ ! -f "${out}" ]; then echo "build_release_epm: ${echoc} did not write an executable to ${out}" >&2 exit 1 fi diff --git a/tools/build_static_curl.sh b/tools/build_static_curl.sh index 8677d42b..b00f5188 100755 --- a/tools/build_static_curl.sh +++ b/tools/build_static_curl.sh @@ -4,10 +4,11 @@ # # tools/build_static_curl.sh /tmp/static-curl # -# HTTP/1.1 + HTTPS only. Both platforms sit on a static OpenSSL we build -# here; Darwin also enables Apple SecTrust so CAs come from the keychain. -# curl 8.21 dropped --with-secure-transport. Homebrew must not win: -# PATH is /usr/bin:/bin plus the prefix, PKG_CONFIG_LIBDIR is the prefix. +# HTTP/1.1 + HTTPS only. Unix sits on a static OpenSSL we build here; +# Darwin also enables Apple SecTrust so CAs come from the keychain. +# Windows uses CMake + Schannel and writes libcurl.lib. curl 8.21 dropped +# --with-secure-transport. Homebrew must not win: PATH is /usr/bin:/bin +# plus the prefix, PKG_CONFIG_LIBDIR is the prefix. set -euo pipefail @@ -20,8 +21,8 @@ prefix="$1" mkdir -p "${prefix}" prefix="$(cd "${prefix}" && pwd)" -if [ -f "${prefix}/lib/libcurl.a" ]; then - echo "build_static_curl: already have ${prefix}/lib/libcurl.a" +if [ -f "${prefix}/lib/libcurl.a" ] || [ -f "${prefix}/lib/libcurl.lib" ]; then + echo "build_static_curl: already have a static libcurl under ${prefix}/lib" exit 0 fi @@ -62,12 +63,18 @@ fetch() { fi } -# replace the search path, do not prepend to Homebrew's -export PKG_CONFIG_LIBDIR="${prefix}/lib/pkgconfig" -unset PKG_CONFIG_PATH -export PATH="${prefix}/bin:/usr/bin:/bin" - os="$(uname -s)" +case "${os}" in + MINGW*|MSYS*|CYGWIN*|Windows_NT) + ;; + *) + # replace the search path, do not prepend to Homebrew's + export PKG_CONFIG_LIBDIR="${prefix}/lib/pkgconfig" + unset PKG_CONFIG_PATH + export PATH="${prefix}/bin:/usr/bin:/bin" + ;; +esac + openssl_target="" curl_tls=(--with-openssl="${prefix}") @@ -90,6 +97,44 @@ case "${os}" in curl_tls+=(--with-apple-sectrust) ;; + MINGW*|MSYS*|CYGWIN*|Windows_NT) + # Schannel is the OS TLS stack, so there is no OpenSSL to build and no + # Autotools. CMake writes libcurl.lib into /lib. + fetch "${CURL_URL}" "${src}/curl.tar.xz" "${CURL_SHA}" + mkdir -p "${src}/curl" + tar -xJf "${src}/curl.tar.xz" -C "${src}/curl" --strip-components=1 + # MultiThreaded is libcmt, the CRT echoc's Windows link line always + # names. CMake defaults to /MD; that archive then pulls ucrt.lib in + # next to libucrt and lld-link duplicate-symbols malloc + cmake -S "${src}/curl" -B "${src}/curl-build" -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${prefix}" \ + -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded \ + -DCMAKE_POLICY_DEFAULT_CMP0091=NEW \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_CURL_EXE=OFF \ + -DCURL_USE_SCHANNEL=ON \ + -DCURL_USE_OPENSSL=OFF \ + -DCURL_USE_LIBPSL=OFF \ + -DCURL_BROTLI=OFF \ + -DCURL_ZSTD=OFF \ + -DCURL_USE_LIBSSH2=OFF \ + -DUSE_NGHTTP2=OFF \ + -DCURL_DISABLE_LDAP=ON \ + -DCURL_DISABLE_RTSP=ON \ + -DENABLE_UNICODE=ON + cmake --build "${src}/curl-build" --config Release + cmake --install "${src}/curl-build" --config Release + + if [ ! -f "${prefix}/lib/libcurl.lib" ] && [ ! -f "${prefix}/lib/libcurl.a" ]; then + echo "build_static_curl: CMake did not write libcurl.lib / libcurl.a into ${prefix}/lib" >&2 + ls -la "${prefix}/lib" 2>/dev/null || true + exit 1 + fi + + exit 0 + ;; + *) echo "build_static_curl: no rule for ${os}" >&2 exit 2 diff --git a/tools/check_release_deps.sh b/tools/check_release_deps.sh index 1d6732c9..82c13bed 100755 --- a/tools/check_release_deps.sh +++ b/tools/check_release_deps.sh @@ -59,6 +59,68 @@ case "$(uname -s)" in fi ;; + MINGW*|MSYS*|CYGWIN*|Windows_NT) + # imports, not paths: the loader looks next to the exe, then System32. A DLL we ship + # beside the binary is the Windows spelling of "the binary carries it". VCRUNTIME / + # MSVCP live in System32 on this runner and on almost no clean machine, so they are + # refused even when present - that is the static CRT not taking. + if command -v llvm-readobj >/dev/null 2>&1; then + deps="$(llvm-readobj --coff-imports "${binary}" | sed -n 's/^ Name: //p')" + else + echo "check_release_deps: llvm-readobj is required on Windows" >&2 + exit 2 + fi + + bindir="$(cd "$(dirname "${binary}")" && pwd)" + sys32="" + if [ -n "${WINDIR:-}" ]; then + sys32="${WINDIR}/System32" + elif [ -d /c/Windows/System32 ]; then + sys32="/c/Windows/System32" + fi + + extra="" + for name in "$@"; do + extra="${extra} ${name}.dll ${name}.DLL" + done + + escaped="" + while IFS= read -r dll; do + [ -z "${dll}" ] && continue + lower="$(printf '%s' "${dll}" | tr '[:upper:]' '[:lower:]')" + case "${lower}" in + vcruntime*.dll|msvcp*.dll|msvcr*.dll|concrt*.dll) + escaped="${escaped}"$'\n'"${dll}" + continue + ;; + esac + + listed=0 + for name in ${extra}; do + extra_lower="$(printf '%s' "${name}" | tr '[:upper:]' '[:lower:]')" + if [ "${lower}" = "${extra_lower}" ]; then + listed=1 + break + fi + done + if [ "${listed}" -eq 1 ]; then + continue + fi + + if [ -f "${bindir}/${dll}" ]; then + continue + fi + + if [ -n "${sys32}" ] && [ -f "${sys32}/${dll}" ]; then + continue + fi + + escaped="${escaped}"$'\n'"${dll}" + done <&2 exit 2 diff --git a/tools/windows/build_release.ps1 b/tools/windows/build_release.ps1 new file mode 100644 index 00000000..37f03dca --- /dev/null +++ b/tools/windows/build_release.ps1 @@ -0,0 +1,281 @@ +# Windows release: two-stage echoc, bundled clang/lld/sysroot, epm, zip, Inno. +# +# Required env: +# LLVM_PATH, BUILD_TYPE, VERSION, ECO_VERSION_PATCH, ECO_VERSION_SUFFIX +# BIN_NAME, EPM_BIN_NAME +# +# ECO_STATIC_ZSTD is on and stays on. A fallback to shared zstd would ship a +# binary that only starts on the runner. + +$ErrorActionPreference = "Stop" + +function Invoke-Checked { + param([scriptblock]$Block, [string]$What) + & $Block + if ($LASTEXITCODE -ne 0) { + Write-Error "$What failed with exit $LASTEXITCODE" + exit $LASTEXITCODE + } +} + +foreach ($name in @("LLVM_PATH", "BUILD_TYPE", "VERSION", "BIN_NAME", "EPM_BIN_NAME")) { + $value = [Environment]::GetEnvironmentVariable($name) + if ([string]::IsNullOrEmpty($value)) { + Write-Error "build_release: $name is not set" + exit 2 + } +} + +function Unix-Path([string]$WinPath) { + $converted = & bash -c "cygpath -u `"$WinPath`"" + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($converted)) { + Write-Error "cygpath failed for $WinPath" + exit 1 + } + return ([string]$converted).Trim() +} + +$root = (Get-Location).Path +$llvmClang = Join-Path $env:LLVM_PATH "bin\clang.exe" +$llvmClangxx = Join-Path $env:LLVM_PATH "bin\clang++.exe" +$llvmDir = Join-Path $env:LLVM_PATH "lib\cmake\llvm" +$patch = $env:ECO_VERSION_PATCH +$suffix = $env:ECO_VERSION_SUFFIX +if ($null -eq $suffix) { $suffix = "" } + +Write-Host "configure stage one" +Invoke-Checked -What "cmake stage one" -Block { + & cmake -B build -G Ninja ` + "-DCMAKE_BUILD_TYPE=$env:BUILD_TYPE" ` + "-DCMAKE_C_COMPILER=$llvmClang" ` + "-DCMAKE_CXX_COMPILER=$llvmClangxx" ` + "-DLLVM_DIR=$llvmDir" +} + +Write-Host "build stage one" +Invoke-Checked -What "build echoc" -Block { + & cmake --build (Join-Path $root "build") --config $env:BUILD_TYPE --target echoc +} + +Write-Host "regenerate embedded stdlib" +New-Item -ItemType File -Path emit.eco -Force | Out-Null +Invoke-Checked -What "emit-stdlib-header" -Block { + & .\build\echoc.exe run --emit-stdlib-header emit.eco +} +Remove-Item emit.eco + +Write-Host "configure release (static zstd required)" +Invoke-Checked -What "cmake release" -Block { + & cmake -B build-release -G Ninja ` + "-DCMAKE_BUILD_TYPE=$env:BUILD_TYPE" ` + "-DCMAKE_C_COMPILER=$llvmClang" ` + "-DCMAKE_CXX_COMPILER=$llvmClangxx" ` + "-DLLVM_DIR=$llvmDir" ` + "-DECO_EMBED_STDLIB=ON" ` + "-DECO_STATIC_ZSTD=ON" ` + "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded" ` + "-DECO_VERSION_PATCH=$patch" ` + "-DECO_VERSION_SUFFIX=$suffix" +} + +Write-Host "build release" +Invoke-Checked -What "build release echoc" -Block { + & cmake --build (Join-Path $root "build-release") --config Release --target echoc +} + +$stage = Join-Path $root "stage" +if (Test-Path -LiteralPath $stage) { + Remove-Item -LiteralPath $stage -Recurse -Force +} +New-Item -ItemType Directory -Path (Join-Path $stage "bin") -Force | Out-Null +Copy-Item (Join-Path $root "build-release\echoc.exe") (Join-Path $stage "bin\echoc.exe") + +Write-Host "bundle clang, lld-link and the Windows sysroot" +$bundle = Join-Path $root "tools\windows\bundle_toolchain.ps1" +Invoke-Checked -What "bundle_toolchain" -Block { + & $bundle $stage +} + +foreach ($name in @("zstd.dll", "libzstd.dll", "zlib.dll", "zlib1.dll")) { + $src = Join-Path $env:LLVM_PATH "bin\$name" + if (Test-Path -LiteralPath $src) { + Copy-Item $src (Join-Path $stage "bin\$name") -Force + } +} + +Copy-Item (Join-Path $root "README.md") (Join-Path $stage "README.md") -Force +Copy-Item (Join-Path $root "LICENSE") (Join-Path $stage "LICENSE") -Force + +function Invoke-WithCleanPath { + param([string]$BinDir, [scriptblock]$Block) + $savedPath = $env:PATH + $savedLlvm = $env:LLVM_PATH + $env:PATH = "$BinDir;C:\Windows\System32;C:\Windows;C:\Windows\System32\Wbem" + Remove-Item Env:LLVM_PATH -ErrorAction SilentlyContinue + try { + & $Block + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + } + finally { + $env:PATH = $savedPath + if ($savedLlvm) { $env:LLVM_PATH = $savedLlvm } + } +} + +$stageBin = Join-Path $stage "bin" +Write-Host "smoke test echoc with a clean PATH (including echoc build)" +Invoke-WithCleanPath -BinDir $stageBin -Block { + $reported = ((& (Join-Path $stageBin "echoc.exe") --version) | Out-String).Trim() + if ($reported -ne $env:VERSION) { + Write-Error "The binary reports '$reported', expected '$env:VERSION'." + exit 1 + } + + $scratch = Join-Path $env:RUNNER_TEMP "echo-smoke" + New-Item -ItemType Directory -Path $scratch -Force | Out-Null + Set-Location $scratch + Set-Content -Path smoke.eco -Value "assert(1 == 1);`nvar `$xs = [1, 2, 3];`ndprint(`$xs[1]);`n" -NoNewline + & (Join-Path $stageBin "echoc.exe") run smoke.eco + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + & (Join-Path $stageBin "echoc.exe") build smoke.eco -o smoke.exe + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + if (-not (Test-Path -LiteralPath "smoke.exe")) { + Write-Error "echoc build did not write smoke.exe" + exit 1 + } + & .\smoke.exe + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +} +Set-Location $root + +Write-Host "check echoc dependencies" +$echocUnix = Unix-Path (Join-Path $stageBin "echoc.exe") +Invoke-Checked -What "check_release_deps echoc" -Block { + bash ./tools/check_release_deps.sh $echocUnix +} + +$curlPrefixWin = Join-Path $env:RUNNER_TEMP "static-curl" +$epmOutWin = Join-Path $env:RUNNER_TEMP "epm" +Write-Host "build static libcurl" +$curlPrefixUnix = Unix-Path $curlPrefixWin +Invoke-Checked -What "build_static_curl" -Block { + bash ./tools/build_static_curl.sh $curlPrefixUnix +} + +Write-Host "build epm" +$epmOutUnix = Unix-Path $epmOutWin +Invoke-Checked -What "build_release_epm" -Block { + bash ./tools/build_release_epm.sh build-release/echoc.exe $epmOutUnix $curlPrefixUnix $env:VERSION +} + +$epmExe = Join-Path $env:RUNNER_TEMP "epm.exe" +if (-not (Test-Path -LiteralPath $epmExe)) { + $epmExe = Join-Path $env:RUNNER_TEMP "epm" +} +Copy-Item $epmExe (Join-Path $stageBin "epm.exe") -Force + +Write-Host "smoke test epm" +$reportedEpm = ((& $epmExe --version) | Out-String).Trim() +if ($reportedEpm -ne $env:VERSION) { + Write-Error "epm reports '$reportedEpm', expected '$env:VERSION'." + exit 1 +} + +Write-Host "check epm dependencies" +$epmCheck = Unix-Path $epmExe +Invoke-Checked -What "check_release_deps epm" -Block { + bash ./tools/check_release_deps.sh $epmCheck +} + +$epmStage = Join-Path $root "epm-stage" +if (Test-Path -LiteralPath $epmStage) { + Remove-Item -LiteralPath $epmStage -Recurse -Force +} +New-Item -ItemType Directory -Path $epmStage -Force | Out-Null +Copy-Item (Join-Path $stageBin "epm.exe") (Join-Path $epmStage "epm.exe") +Copy-Item (Join-Path $root "LICENSE") (Join-Path $epmStage "LICENSE") + +Write-Host "package zip archives" +Compress-Archive -Path (Join-Path $stage "*") -DestinationPath (Join-Path $root "$env:BIN_NAME.zip") -Force +Compress-Archive -Path (Join-Path $epmStage "*") -DestinationPath (Join-Path $root "$env:EPM_BIN_NAME.zip") -Force + +Write-Host "build setup wizard" +$iscc = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe" +if (-not (Test-Path -LiteralPath $iscc)) { + $iscc = "${env:ProgramFiles}\Inno Setup 6\ISCC.exe" +} +if (-not (Test-Path -LiteralPath $iscc)) { + Write-Error "ISCC.exe not found; install Inno Setup 6" + exit 1 +} +Invoke-Checked -What "ISCC" -Block { + & $iscc "/DAppVersion=$env:VERSION" "/DStageDir=$stage" "/O$root" (Join-Path $root "tools\windows\echo.iss") +} +$setup = Join-Path $root "echo-windows-x86_64-setup.exe" +if (-not (Test-Path -LiteralPath $setup)) { + Write-Error "ISCC did not write echo-windows-x86_64-setup.exe" + exit 1 +} + +Write-Host "smoke test the setup wizard" +$setupDir = Join-Path $env:RUNNER_TEMP "echo-setup-smoke" +$setupLog = Join-Path $env:RUNNER_TEMP "echo-setup-smoke.log" +$installed = Join-Path $setupDir "bin\echoc.exe" + +# Inno's Setup.exe is a GUI subsystem binary. `&` in PowerShell does not wait +# for those, so LASTEXITCODE was still ISCC's 0 and the next line looked for +# echoc 136ms later. Start-Process -Wait is the wait; the poll is in case the +# stub returns before the inner installer has finished copying the sysroot +$proc = Start-Process -FilePath $setup -ArgumentList @( + "/VERYSILENT", + "/NORESTART", + "/SUPPRESSMSGBOXES", + "/CURRENTUSER", + "/DIR=$setupDir", + "/LOG=$setupLog" +) -Wait -PassThru +if ($null -eq $proc.ExitCode -or $proc.ExitCode -ne 0) { + if (Test-Path -LiteralPath $setupLog) { Get-Content -LiteralPath $setupLog } + Write-Error "silent setup exited $($proc.ExitCode)" + exit 1 +} + +$deadline = (Get-Date).AddMinutes(15) +while (-not (Test-Path -LiteralPath $installed)) { + if ((Get-Date) -gt $deadline) { + if (Test-Path -LiteralPath $setupLog) { Get-Content -LiteralPath $setupLog } + if (Test-Path -LiteralPath $setupDir) { + Get-ChildItem -LiteralPath $setupDir -Recurse -ErrorAction SilentlyContinue | + Select-Object -First 80 -ExpandProperty FullName + } + $fallback = Join-Path $env:LOCALAPPDATA "echo\bin\echoc.exe" + if (Test-Path -LiteralPath $fallback) { + Write-Error "silent setup wrote $fallback instead of $installed (/DIR was ignored)" + } else { + Write-Error "silent setup did not write $installed" + } + exit 1 + } + Start-Sleep -Seconds 2 +} + +Invoke-WithCleanPath -BinDir (Join-Path $setupDir "bin") -Block { + $reported = ((& $installed --version) | Out-String).Trim() + if ($reported -ne $env:VERSION) { + Write-Error "setup-installed echoc reports '$reported', expected '$env:VERSION'." + exit 1 + } + $scratch = Join-Path $env:RUNNER_TEMP "echo-setup-build" + New-Item -ItemType Directory -Path $scratch -Force | Out-Null + Set-Location $scratch + Set-Content -Path hello.eco -Value "echo 1;`n" -NoNewline + & $installed build hello.eco -o hello.exe + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + & .\hello.exe + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +} +Set-Location $root + +Write-Host "windows release ok: $env:VERSION" diff --git a/tools/windows/bundle_toolchain.ps1 b/tools/windows/bundle_toolchain.ps1 new file mode 100644 index 00000000..bb266ca6 --- /dev/null +++ b/tools/windows/bundle_toolchain.ps1 @@ -0,0 +1,167 @@ +# Copy clang, lld-link and a Windows sysroot next to a released echoc so +# `echoc build` works on a machine that has never installed LLVM or VS. +# +# tools/windows/bundle_toolchain.ps1 +# +# dest-root is the stage directory: bin/, lib/clang/, sysroot/{lib,include}. +# LLVM_PATH, LIB and INCLUDE come from the MSVC+LLVM environment the release +# job already has. A missing sysroot is a hard failure: that would ship a +# compiler that cannot link. + +$ErrorActionPreference = "Stop" + +if ($args.Count -ne 1) { + Write-Error "usage: bundle_toolchain.ps1 " + exit 2 +} + +$DestRoot = $args[0] +if (-not $env:LLVM_PATH) { + Write-Error "bundle_toolchain: LLVM_PATH is not set" + exit 2 +} + +$llvmBin = Join-Path $env:LLVM_PATH "bin" +$destBin = Join-Path $DestRoot "bin" +$destLib = Join-Path $DestRoot "lib" +$destSysroot = Join-Path $DestRoot "sysroot" +$destSysLib = Join-Path $destSysroot "lib" +$destSysInc = Join-Path $destSysroot "include" + +New-Item -ItemType Directory -Path $destBin, $destLib, $destSysLib, $destSysInc -Force | Out-Null + +$tools = @("clang.exe", "lld.exe", "lld-link.exe", "llvm-nm.exe", "llvm-ar.exe") +foreach ($name in $tools) { + $src = Join-Path $llvmBin $name + if (-not (Test-Path -LiteralPath $src)) { + Write-Error "bundle_toolchain: $src is missing" + exit 1 + } + Copy-Item -LiteralPath $src -Destination (Join-Path $destBin $name) -Force +} + +$readobj = Join-Path $llvmBin "llvm-readobj.exe" +function Get-Imports([string]$Exe) { + if (-not (Test-Path -LiteralPath $readobj)) { + return @() + } + $names = @() + & $readobj --coff-imports $Exe | ForEach-Object { + if ($_ -match '^\s+Name:\s+(\S+\.dll)$') { + $names += $Matches[1] + } + } + return $names +} + +$pending = New-Object System.Collections.Generic.Queue[string] +Get-ChildItem -LiteralPath $destBin -Filter "*.exe" -ErrorAction SilentlyContinue | ForEach-Object { + $pending.Enqueue($_.FullName) +} +$copiedDll = @{} +while ($pending.Count -gt 0) { + $exe = $pending.Dequeue() + foreach ($dll in Get-Imports $exe) { + $key = $dll.ToLowerInvariant() + if ($copiedDll.ContainsKey($key)) { + continue + } + $src = Join-Path $llvmBin $dll + if (-not (Test-Path -LiteralPath $src)) { + continue + } + $dest = Join-Path $destBin $dll + Copy-Item -LiteralPath $src -Destination $dest -Force + $copiedDll[$key] = $true + $pending.Enqueue($dest) + } +} + +$clangLib = Join-Path $env:LLVM_PATH "lib\clang" +if (-not (Test-Path -LiteralPath $clangLib)) { + Write-Error "bundle_toolchain: no clang resource directory at $clangLib" + exit 1 +} +Copy-Item -LiteralPath $clangLib -Destination (Join-Path $destLib "clang") -Recurse -Force + +if (-not $env:LIB) { + Write-Error "bundle_toolchain: LIB is empty; msvc-dev-cmd did not run" + exit 1 +} + +$libCount = 0 +foreach ($dir in ($env:LIB -split ";" | Where-Object { $_ -and $_.Trim().Length -gt 0 })) { + if (-not (Test-Path -LiteralPath $dir)) { + continue + } + Get-ChildItem -LiteralPath $dir -Filter "*.lib" -ErrorAction SilentlyContinue | ForEach-Object { + Copy-Item -LiteralPath $_.FullName -Destination (Join-Path $destSysLib $_.Name) -Force + $libCount += 1 + } +} + +if ($libCount -eq 0) { + Write-Error "bundle_toolchain: copied no .lib files from LIB=$env:LIB" + exit 1 +} + +foreach ($must in @("libcmt.lib", "libucrt.lib", "libvcruntime.lib", "oldnames.lib", "kernel32.lib")) { + if (-not (Test-Path -LiteralPath (Join-Path $destSysLib $must))) { + Write-Error "bundle_toolchain: $must is missing from $destSysLib" + exit 1 + } +} + +if (-not $env:INCLUDE) { + Write-Error "bundle_toolchain: INCLUDE is empty; msvc-dev-cmd did not run" + exit 1 +} + +function Include-Leaf([string]$Path) { + # Split-Path -LiteralPath -Leaf is not a parameter set, in Windows PowerShell + # or in pwsh. GetFileName does not glob and does not need a trailing-slash dance + $leaf = [System.IO.Path]::GetFileName($Path.TrimEnd('\', '/')) + if ($leaf -eq "winrt" -or $leaf -eq "cppwinrt" -or $leaf -eq "atl" -or $leaf -eq "mfc") { + return $null + } + if ($leaf -eq "ucrt" -or $leaf -eq "um" -or $leaf -eq "shared") { + return $leaf + } + if ($Path -match "MSVC") { + return "msvc" + } + if ($Path -match "clang") { + return $null + } + return $leaf +} + +$incCount = 0 +$usedLeaf = @{} +foreach ($dir in ($env:INCLUDE -split ";" | Where-Object { $_ -and $_.Trim().Length -gt 0 })) { + if (-not (Test-Path -LiteralPath $dir)) { + continue + } + $leaf = Include-Leaf $dir + if ($null -eq $leaf) { + continue + } + if ($usedLeaf.ContainsKey($leaf)) { + $n = 2 + while ($usedLeaf.ContainsKey("$leaf-$n")) { + $n += 1 + } + $leaf = "$leaf-$n" + } + $usedLeaf[$leaf] = $true + $dest = Join-Path $destSysInc $leaf + Copy-Item -LiteralPath $dir -Destination $dest -Recurse -Force + $incCount += 1 +} + +if ($incCount -eq 0) { + Write-Error "bundle_toolchain: copied no include directories from INCLUDE=$env:INCLUDE" + exit 1 +} + +Write-Host "bundle_toolchain: $libCount import libraries, $incCount include trees, $($copiedDll.Count) DLLs" diff --git a/tools/windows/echo.iss b/tools/windows/echo.iss new file mode 100644 index 00000000..036d3ed0 --- /dev/null +++ b/tools/windows/echo.iss @@ -0,0 +1,129 @@ +; GUI wizard for Echo. Built by the Windows release job: +; +; iscc /DAppVersion=0.2.3 /DStageDir=... tools/windows/echo.iss +; +; binaries go in {app}\bin, which defaults to %LOCALAPPDATA%\echo\bin, the +; same directory install.ps1 uses. clang, lld-link and sysroot sit next to +; them so `echoc build` needs no separate LLVM. per-user, no admin. + +#ifndef AppVersion + #define AppVersion "0.0.0" +#endif +#ifndef StageDir + #define StageDir "stage" +#endif + +#define AppName "Echo" +#define AppPublisher "echolang" +#define AppURL "https://github.com/echolang/echo" + +[Setup] +AppId={{E4C80A31-9B7F-4D2A-A6F1-3C8E5B7A1D90} +AppName={#AppName} +AppVersion={#AppVersion} +AppPublisher={#AppPublisher} +AppPublisherURL={#AppURL} +AppSupportURL={#AppURL} +AppUpdatesURL={#AppURL}/releases +DefaultDirName={localappdata}\echo +DefaultGroupName={#AppName} +DisableProgramGroupPage=yes +LicenseFile={#StageDir}\LICENSE +OutputDir=. +OutputBaseFilename=echo-windows-x86_64-setup +Compression=lzma2 +SolidCompression=yes +WizardStyle=modern +PrivilegesRequired=lowest +ChangesEnvironment=yes +UninstallDisplayName={#AppName} {#AppVersion} +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible +MinVersion=10.0 +SetupLogging=yes +UsePreviousAppDir=no + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Files] +Source: "{#StageDir}\bin\*"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "{#StageDir}\lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion recursesubdirs createallsubdirs skipifsourcedoesntexist +Source: "{#StageDir}\sysroot\*"; DestDir: "{app}\sysroot"; Flags: ignoreversion recursesubdirs createallsubdirs skipifsourcedoesntexist +Source: "{#StageDir}\LICENSE"; DestDir: "{app}"; Flags: ignoreversion +Source: "{#StageDir}\README.md"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist + +[Messages] +FinishedLabel=Echo is installed. Open a new terminal and run echoc --version. + +[Code] +function NeedsAddPath(BinDir: string): Boolean; +var + Current: string; + Needle: string; +begin + Result := True; + if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Current) then + Exit; + Needle := ';' + Uppercase(BinDir) + ';'; + if Pos(Needle, ';' + Uppercase(Current) + ';') > 0 then + Result := False; +end; + +procedure AddToUserPath(BinDir: string); +var + Current: string; +begin + if not NeedsAddPath(BinDir) then + Exit; + if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Current) then + Current := ''; + if Current = '' then + Current := BinDir + else if Current[Length(Current)] = ';' then + Current := Current + BinDir + else + Current := Current + ';' + BinDir; + RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Current); +end; + +procedure RemoveFromUserPath(BinDir: string); +var + Current, Entry, ResultPath: string; + StartPos, Semi: Integer; + Want: string; +begin + if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Current) then + Exit; + Want := Uppercase(BinDir); + ResultPath := ''; + Current := Current + ';'; + StartPos := 1; + while StartPos <= Length(Current) do + begin + Semi := StartPos; + while (Semi <= Length(Current)) and (Current[Semi] <> ';') do + Semi := Semi + 1; + Entry := Copy(Current, StartPos, Semi - StartPos); + if (Entry <> '') and (Uppercase(Entry) <> Want) then + begin + if ResultPath <> '' then + ResultPath := ResultPath + ';'; + ResultPath := ResultPath + Entry; + end; + StartPos := Semi + 1; + end; + RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', ResultPath); +end; + +procedure CurStepChanged(CurStep: TSetupStep); +begin + if CurStep = ssPostInstall then + AddToUserPath(ExpandConstant('{app}\bin')); +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +begin + if CurUninstallStep = usPostUninstall then + RemoveFromUserPath(ExpandConstant('{app}\bin')); +end;